目录
🚀 高性能线程安全的时间有序 UUID 生成器 —— 基于 Java ThreadLocal
的实现
在分布式系统中,我们经常需要生成 全局唯一 且 有序 的 ID,比如用于数据库主键、消息系统的唯一标识符、日志追踪等场景。Java 原生的 UUID.randomUUID()
虽然能生成唯一 ID,但它是随机的,不具备时间顺序性,作为数据库主键的效率会大大降低。
本文将介绍一个高性能、线程安全的时间有序 UUID 工具类:UuidUtils
,它具备以下特性:
- ✅ 时间有序,便于排序和归档
- ✅ 高并发性能,使用
ThreadLocal
避免全局锁 - ✅ UUID 无横杠(32位十六进制字符串)
- ✅ 可解析时间戳、序列号、线程ID
- ✅ 支持并发测试、性能测试、可视化展示
🔧 核心思路
我们构造的 UUID 分为两个部分(共 128 位):
部分 | 位数 | 内容 |
---|---|---|
Most Significant Bits(高位) | 48 | 当前毫秒时间戳 |
4 | 版本号(固定为 7) | |
12 | 当前线程的序列号 | |
Least Significant Bits(低位) | 2 | Variant(固定) |
8 | 线程ID(保证跨线程唯一) | |
10 | 机器ID(用于分布式环境) | |
44 | 随机填充 |
通过这种结构,既保留了 UUID 的随机性,又引入了时间戳、线程ID等标识,有助于定位问题和排序数据。
🚀 高性能时间有序 UUID 生成器(无横杠)——Java 实现与详解
在高并发分布式系统中,生成唯一、时间有序且高性能的 ID 是一个非常常见的需求。Java 自带的 UUID.randomUUID()
属于 UUID v4(基于随机数),虽然唯一性较高,但无序且冗长(带横杠),在日志、数据库索引等场景下并不友好。
本篇博客将带你深入理解并实战一个 ThreadLocal 高性能时间有序 UUID 生成器,具备以下特性:
✅ 核心特性
- 时间有序:以毫秒为单位,确保按时间排序。
- 高性能:每线程独立状态(ThreadLocal 免锁机制),无锁操作。
- 无横杠 UUID:更紧凑,便于存储和传输。
- 线程 ID / 机器 ID / 序列号嵌入:增强可溯性与分布式唯一性。
- 时钟回拨保护:防止 UUID 重复。
- 支持提取时间戳、线程 ID 等结构字段。
- 并发测试验证无冲突。
✨ 核心设计理念
UUID 由 128 位组成,结构如下:
高 64 位(mostSigBits) | 低 64 位(leastSigBits) |
---|---|
时间戳(48位)+ 版本(4位)+ 序列号(12位) | variant(2位)+ 线程 ID(8位)+ 机器 ID(10位)+ 随机数(44位) |
我们将版本号强制设置为 0x7
(UUID v7 标志),用于识别自定义 UUID。其余位组合了时间、线程、机器和随机性,确保高并发唯一性且可解析。
🧩 应用场景
- 高并发日志追踪 ID(按时间排序)
- 分布式系统的业务主键(可支持 Sharding)
- 全局唯一 ID,但又不想使用复杂方案(如 Snowflake)
- 替代 UUID v4 的简洁、结构化方案
📦 完整 Java 实现代码
以下是完整源码,关键逻辑均附有中文注释说明:
import java.security.SecureRandom;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
/**
* 时间有序UUID生成器工具类 - ThreadLocal版本(无横杠)
*
* 改进方案:
* 1. 使用ThreadLocal避免全局锁,提高并发性能
* 2. 每个线程维护自己的时间戳和序列号
* 3. 加入线程ID确保跨线程唯一性
* 4. 保持时钟回拨检测
* 5. 生成不带横杠的UUID字符串
*/
public class UuidUtils {
private static final SecureRandom SECURE_RANDOM = new SecureRandom();
// 机器ID(可选,用于分布式环境)
private static final long MACHINE_ID = generateMachineId();
// 序列号掩码(12位)
private static final long SEQUENCE_MASK = 0xFFF;
// 线程ID计数器
private static final AtomicLong THREAD_ID_COUNTER = new AtomicLong(0);
// 十六进制字符数组
private static final char[] HEX_CHARS = "0123456789abcdef".toCharArray();
// 版本号掩码(4位)
private static final long VERSION_MASK = 0x7000L;
// Variant掩码(2位)
private static final long VARIANT_MASK = 0x8000000000000000L;
// 线程ID掩码(8位)
private static final long THREAD_ID_MASK = 0xFFL;
private static final int THREAD_ID_SHIFT = 32;
// 机器ID掩码(10位)
private static final long MACHINE_ID_MASK = 0x3FFL;
private static final int MACHINE_ID_SHIFT = 32;
/**
* 线程本地存储的UUID生成器状态
*/
private static class ThreadLocalState {
// 上次生成UUID的时间戳
long lastTimestamp = 0L;
// 同一毫秒内的序列号
long sequence = 0L;
// 线程唯一ID(8位,最大255)
final long threadId;
ThreadLocalState() {
this.threadId = THREAD_ID_COUNTER.getAndIncrement() & 0xFF;
}
}
// ThreadLocal存储每个线程的状态
private static final ThreadLocal<ThreadLocalState> THREAD_LOCAL_STATE =
ThreadLocal.withInitial(ThreadLocalState::new);
/**
* 生成保证唯一的时间有序UUID(不带横杠)
*
* @return 32位不带横杠的UUID字符串
*/
public static String generateUniqueTimeOrderedUuid() {
UUID uuid = generateUniqueTimeOrderedUuidObject();
return uuidToNoDashString(uuid);
}
/**
* 生成保证唯一的时间有序UUID对象
*
* UUID结构:
* - 时间戳(48位) + 版本号(4位) + 序列号(12位)
* - Variant(2位) + 线程ID(8位) + 机器ID(10位) + 随机数(44位)
*/
public static UUID generateUniqueTimeOrderedUuidObject() {
ThreadLocalState state = THREAD_LOCAL_STATE.get();
long currentTimestamp = System.currentTimeMillis();
// 检测时钟回拨
if (currentTimestamp < state.lastTimestamp) {
throw new RuntimeException(String.format(
"时钟回拨检测! 当前时间: %d, 上次时间: %d, 线程ID: %d",
currentTimestamp, state.lastTimestamp, state.threadId));
}
long currentSequence;
if (currentTimestamp == state.lastTimestamp) {
// 同一毫秒内,序列号递增
state.sequence = (state.sequence + 1) & SEQUENCE_MASK;
currentSequence = state.sequence;
// 序列号溢出,等待下一毫秒
if (currentSequence == 0) {
currentTimestamp = waitForNextMillis(currentTimestamp);
}
} else {
// 新的毫秒,序列号重置
state.sequence = 0;
currentSequence = 0;
}
state.lastTimestamp = currentTimestamp;
return buildUuid(currentTimestamp, currentSequence, state.threadId);
}
/**
* 构建UUID
*/
private static UUID buildUuid(long timestamp, long sequence, long threadId) {
// 生成随机数
byte[] randomBytes = new byte[6];
SECURE_RANDOM.nextBytes(randomBytes);
// 前64位:时间戳(48位) + 版本号(4位) + 序列号(12位),long类型最终都是64位,高位0 不显示
long mostSigBits = (timestamp << 16) | VERSION_MASK | sequence;
// 后64位:variant(2位) + 线程ID(8位) + 机器ID(10位) + 随机数(44位)
long leastSigBits = VARIANT_MASK |
((threadId & THREAD_ID_MASK) << THREAD_ID_SHIFT) |
((MACHINE_ID & MACHINE_ID_MASK) << MACHINE_ID_SHIFT) |
((randomBytes[0] & 0xFFL) << 36) |
((randomBytes[1] & 0xFFL) << 28) |
((randomBytes[2] & 0xFFL) << 20) |
((randomBytes[3] & 0xFFL) << 12) |
((randomBytes[4] & 0xFFL) << 4) |
(randomBytes[5] & 0x0FL);
return new UUID(mostSigBits, leastSigBits);
}
/**
* 将UUID转换为不带横杠的字符串
*/
private static String uuidToNoDashString(UUID uuid) {
long mostSigBits = uuid.getMostSignificantBits();
long leastSigBits = uuid.getLeastSignificantBits();
char[] chars = new char[32];
// 转换mostSigBits (前16个字符)
for (int i = 15; i >= 0; i--) {
chars[i] = HEX_CHARS[(int) (mostSigBits & 0xF)];
mostSigBits >>>= 4;
}
// 转换leastSigBits (后16个字符)
for (int i = 31; i >= 16; i--) {
chars[i] = HEX_CHARS[(int) (leastSigBits & 0xF)];
leastSigBits >>>= 4;
}
return new String(chars);
}
/**
* 将不带横杠的UUID字符串转换为UUID对象
*/
public static UUID parseNoDashUuid(String noDashUuid) {
if (noDashUuid == null || noDashUuid.length() != 32) {
throw new IllegalArgumentException("无效的UUID字符串,长度必须为32位");
}
// 验证是否为有效的十六进制字符串
for (char c : noDashUuid.toCharArray()) {
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))) {
throw new IllegalArgumentException("无效的UUID字符串,包含非十六进制字符: " + c);
}
}
String mostSigBitsStr = noDashUuid.substring(0, 16);
String leastSigBitsStr = noDashUuid.substring(16, 32);
long mostSigBits = Long.parseUnsignedLong(mostSigBitsStr, 16);
long leastSigBits = Long.parseUnsignedLong(leastSigBitsStr, 16);
return new UUID(mostSigBits, leastSigBits);
}
/**
* 等待下一毫秒
*/
private static long waitForNextMillis(long currentTimestamp) {
while (System.currentTimeMillis() <= currentTimestamp) {
// 自旋等待
}
return System.currentTimeMillis();
}
/**
* 生成机器ID(简化版)
*/
private static long generateMachineId() {
try {
// 基于MAC地址或主机名生成
java.net.InetAddress addr = java.net.InetAddress.getLocalHost();
byte[] mac = java.net.NetworkInterface.getByInetAddress(addr).getHardwareAddress();
if (mac != null) {
return ((long)(mac[mac.length - 2] & 0xFF) << 8) |
(long)(mac[mac.length - 1] & 0xFF);
}
} catch (Exception e) {
// fallback to random
}
return SECURE_RANDOM.nextInt(1024);
}
/**
* 从UUID字符串中提取时间戳
*/
public static long extractTimestamp(String noDashUuid) {
UUID uuid = parseNoDashUuid(noDashUuid);
return uuid.getMostSignificantBits() >> 16;
}
/**
* 从UUID字符串中提取序列号
*/
public static long extractSequence(String noDashUuid) {
UUID uuid = parseNoDashUuid(noDashUuid);
return uuid.getMostSignificantBits() & SEQUENCE_MASK;
}
/**
* 从UUID字符串中提取线程ID
*/
public static long extractThreadId(String noDashUuid) {
UUID uuid = parseNoDashUuid(noDashUuid);
return (uuid.getLeastSignificantBits() >> 32) & 0xFF;
}
/**
* 从UUID对象中提取时间戳
*/
public static long extractTimestamp(UUID uuid) {
return uuid.getMostSignificantBits() >> 16;
}
/**
* 从UUID对象中提取序列号
*/
public static long extractSequence(UUID uuid) {
return uuid.getMostSignificantBits() & SEQUENCE_MASK;
}
/**
* 从UUID对象中提取线程ID
*/
public static long extractThreadId(UUID uuid) {
return (uuid.getLeastSignificantBits() >> 32) & 0xFF;
}
/**
* 批量生成UUID(不带横杠)
*/
public static List<String> generateBatch(int count) {
List<String> uuids = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
uuids.add(generateUniqueTimeOrderedUuid());
}
return uuids;
}
/**
* 批量生成UUID对象
*/
public static List<UUID> generateBatchObjects(int count) {
List<UUID> uuids = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
uuids.add(generateUniqueTimeOrderedUuidObject());
}
return uuids;
}
/**
* 验证UUID是否由此生成器生成
*/
public static boolean isTimeOrderedUuid(String noDashUuid) {
try {
UUID uuid = parseNoDashUuid(noDashUuid);
return isTimeOrderedUuid(uuid);
} catch (Exception e) {
return false;
}
}
/**
* 验证UUID对象是否由此生成器生成
*/
public static boolean isTimeOrderedUuid(UUID uuid) {
// 检查版本号是否为7
return (uuid.getMostSignificantBits() & 0xF000) == 0x7000;
}
/**
* 获取当前线程的统计信息
*/
public static String getThreadStats() {
ThreadLocalState state = THREAD_LOCAL_STATE.get();
return String.format("线程ID: %d, 上次时间戳: %d, 当前序列号: %d",
state.threadId, state.lastTimestamp, state.sequence);
}
/**
* 重置当前线程的状态(测试用)
*/
public static void resetThreadState() {
THREAD_LOCAL_STATE.remove();
}
/**
* 测试性能
*/
public static void testPerformance() {
System.out.println("=== 性能测试 ===");
int testCount = 100000;
// 测试ThreadLocal版本的时间有序UUID(不带横杠)
long startTime = System.currentTimeMillis();
for (int i = 0; i < testCount; i++) {
generateUniqueTimeOrderedUuid();
}
long threadLocalTime = System.currentTimeMillis() - startTime;
// 测试标准UUID v4
startTime = System.currentTimeMillis();
for (int i = 0; i < testCount; i++) {
UUID.randomUUID().toString().replace("-", "");
}
long standardTime = System.currentTimeMillis() - startTime;
System.out.printf("生成 %d 个UUID的性能对比:%n", testCount);
System.out.printf("ThreadLocal时间有序UUID(无横杠): %d ms%n", threadLocalTime);
System.out.printf("标准UUID v4(转换无横杠): %d ms%n", standardTime);
System.out.printf("性能比率: %.2fx%n", (double)threadLocalTime / standardTime);
System.out.println();
}
/**
* 测试唯一性
*/
public static void testUniqueness() {
System.out.println("=== 唯一性测试 ===");
Set<String> uuidSet = new HashSet<>();
int testCount = 100000;
long startTime = System.currentTimeMillis();
for (int i = 0; i < testCount; i++) {
String uuid = generateUniqueTimeOrderedUuid();
if (!uuidSet.add(uuid)) {
System.out.println("❌ 发现重复UUID: " + uuid);
return;
}
}
long endTime = System.currentTimeMillis();
System.out.printf("✅ 生成 %d 个UUID,全部唯一%n", testCount);
System.out.printf(" 耗时: %d ms%n", endTime - startTime);
System.out.printf(" 集合大小验证: %d%n", uuidSet.size());
System.out.println();
}
/**
* 测试并发安全性
*/
public static void testConcurrency() {
System.out.println("=== 并发安全性测试 ===");
ConcurrentHashMap<String, Boolean> concurrentSet = new ConcurrentHashMap<>();
int threadCount = 10;
int perThreadCount = 10000;
CountDownLatch latch = new CountDownLatch(threadCount);
AtomicInteger duplicateCount = new AtomicInteger(0);
long startTime = System.currentTimeMillis();
// 创建多个线程并发生成UUID
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(() -> {
for (int j = 0; j < perThreadCount; j++) {
String uuid = generateUniqueTimeOrderedUuid();
if (concurrentSet.putIfAbsent(uuid, true) != null) {
duplicateCount.incrementAndGet();
System.out.println("❌ 并发测试发现重复UUID: " + uuid);
}
}
latch.countDown();
});
thread.start();
}
try {
latch.await();
long endTime = System.currentTimeMillis();
int totalGenerated = threadCount * perThreadCount;
System.out.printf("✅ 并发测试完成%n");
System.out.printf(" 线程数: %d%n", threadCount);
System.out.printf(" 每线程生成: %d%n", perThreadCount);
System.out.printf(" 总生成数: %d%n", totalGenerated);
System.out.printf(" 实际唯一数: %d%n", concurrentSet.size());
System.out.printf(" 重复数: %d%n", duplicateCount.get());
System.out.printf(" 耗时: %d ms%n", endTime - startTime);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println();
}
/**
* 测试字符串转换功能
*/
public static void testStringConversion() {
System.out.println("=== 字符串转换测试 ===");
for (int i = 0; i < 5; i++) {
// 生成UUID
String noDashUuid = generateUniqueTimeOrderedUuid();
UUID originalUuid = generateUniqueTimeOrderedUuidObject();
String convertedNoDash = uuidToNoDashString(originalUuid);
System.out.printf("测试 %d:%n", i + 1);
System.out.printf(" 直接生成(无横杠): %s%n", noDashUuid);
System.out.printf(" 对象转换(无横杠): %s%n", convertedNoDash);
System.out.printf(" 原始UUID对象: %s%n", originalUuid);
// 测试往返转换
try {
UUID parsedUuid = parseNoDashUuid(noDashUuid);
String backToNoDash = uuidToNoDashString(parsedUuid);
System.out.printf(" 往返转换结果: %s%n", backToNoDash);
System.out.printf(" 往返转换正确: %s%n", noDashUuid.equals(backToNoDash) ? "✅" : "❌");
// 提取信息测试
long timestamp = extractTimestamp(noDashUuid);
long sequence = extractSequence(noDashUuid);
long threadId = extractThreadId(noDashUuid);
System.out.printf(" 时间戳: %d (%s)%n", timestamp, java.time.Instant.ofEpochMilli(timestamp));
System.out.printf(" 序列号: %d%n", sequence);
System.out.printf(" 线程ID: %d%n", threadId);
System.out.printf(" 是否时间有序: %s%n", isTimeOrderedUuid(noDashUuid) ? "✅" : "❌");
} catch (Exception e) {
System.out.printf(" ❌ 转换失败: %s%n", e.getMessage());
}
System.out.println();
}
}
/**
* 使用示例
*/
public static void demonstrateUsage() {
System.out.println("=== 使用示例 ===");
System.out.println("生成的UUID序列(不带横杠,注意时间有序性):");
for (int i = 0; i < 10; i++) {
String uuid = generateUniqueTimeOrderedUuid();
long timestamp = extractTimestamp(uuid);
long sequence = extractSequence(uuid);
long threadId = extractThreadId(uuid);
System.out.printf("%d. %s%n", i + 1, uuid);
System.out.printf(" 时间戳: %d (%s)%n",
timestamp,
java.time.Instant.ofEpochMilli(timestamp));
System.out.printf(" 序列号: %d%n", sequence);
System.out.printf(" 线程ID: %d%n", threadId);
System.out.println();
// 小延迟以观察不同时间戳
if (i % 3 == 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
System.out.println("当前线程状态: " + getThreadStats());
}
/**
* 多线程演示
*/
public static void demonstrateMultiThreadUsage() {
System.out.println("=== 多线程使用示例 ===");
CountDownLatch latch = new CountDownLatch(3);
for (int t = 0; t < 3; t++) {
final int threadNum = t;
new Thread(() -> {
System.out.printf("线程 %d 开始生成UUID:%n", threadNum);
for (int i = 0; i < 5; i++) {
String uuid = generateUniqueTimeOrderedUuid();
System.out.printf(" 线程%d-%d: %s (线程ID: %d)%n",
threadNum, i + 1, uuid, extractThreadId(uuid));
}
System.out.printf("线程 %d 状态: %s%n", threadNum, getThreadStats());
latch.countDown();
}).start();
}
try {
latch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println();
}
public static void main(String[] args) {
// 测试所有功能
testUniqueness();
testPerformance();
testConcurrency();
testStringConversion();
demonstrateUsage();
demonstrateMultiThreadUsage();
}
}
🔍 关键功能说明
✅ 1. 无锁高并发:使用 ThreadLocal 管理线程状态
private static final ThreadLocal<ThreadLocalState> THREAD_LOCAL_STATE =
ThreadLocal.withInitial(ThreadLocalState::new);
- 每个线程拥有独立的
lastTimestamp
与sequence
,避免竞争和锁开销。 - 线程 ID 使用
AtomicLong
自增生成。
⏱️ 2. 时间戳 + 序列号控制顺序与唯一性
if (currentTimestamp == state.lastTimestamp) {
// 同一毫秒内增加序列号
state.sequence = (state.sequence + 1) & SEQUENCE_MASK;
}
- 若同一毫秒生成多条 UUID,使用序列号保证唯一。
- 若序列号溢出(最多 4096 个),自旋等待下一毫秒。
🧪 3. 内置测试方法
为了验证这个生成器的性能和稳定性,类中提供了多个测试方法:
testUniqueness()
:验证 10 万个 UUID 无重复testPerformance()
:与标准 UUID v4 性能对比testConcurrency()
:多线程并发验证唯一性testStringConversion()
:往返转换、字段提取测试demonstrateUsage()
/demonstrateMultiThreadUsage()
:演示单线程与多线程用法
🚀 性能对比
以下是一些使用示例输出片段:
=== 唯一性测试 ===
✅ 生成 100000 个UUID,全部唯一
耗时: 147 ms
集合大小验证: 100000
=== 性能测试 ===
生成 100000 个UUID的性能对比:
ThreadLocal时间有序UUID(无横杠): 32 ms
标准UUID v4(转换无横杠): 181 ms
性能比率: 0.18x
=== 并发安全性测试 ===
✅ 并发测试完成
线程数: 10
每线程生成: 10000
总生成数: 100000
实际唯一数: 100000
重复数: 0
耗时: 66 ms
=== 字符串转换测试 ===
测试 1:
直接生成(无横杠): 01982fc0065b700080137b5863622162
对象转换(无横杠): 01982fc0065b700180137274fbc25fb5
原始UUID对象: 01982fc0-065b-7001-8013-7274fbc25fb5
往返转换结果: 01982fc0065b700080137b5863622162
往返转换正确: ✅
时间戳: 1753147770459 (2025-07-22T01:29:30.459Z)
序列号: 0
线程ID: 0
是否时间有序: ✅
测试 2:
直接生成(无横杠): 01982fc0066c700080137fa6724fffc2
对象转换(无横杠): 01982fc0066c70018013704eee60d1d0
原始UUID对象: 01982fc0-066c-7001-8013-704eee60d1d0
往返转换结果: 01982fc0066c700080137fa6724fffc2
往返转换正确: ✅
时间戳: 1753147770476 (2025-07-22T01:29:30.476Z)
序列号: 0
线程ID: 0
是否时间有序: ✅
测试 3:
直接生成(无横杠): 01982fc0066d700080137ebb64c59537
对象转换(无横杠): 01982fc0066d70018013732f35dba80c
原始UUID对象: 01982fc0-066d-7001-8013-732f35dba80c
往返转换结果: 01982fc0066d700080137ebb64c59537
往返转换正确: ✅
时间戳: 1753147770477 (2025-07-22T01:29:30.477Z)
序列号: 0
线程ID: 0
是否时间有序: ✅
测试 4:
直接生成(无横杠): 01982fc0066e7000801373cdf1e15e27
对象转换(无横杠): 01982fc0066e700180137cbb81eba9ea
原始UUID对象: 01982fc0-066e-7001-8013-7cbb81eba9ea
往返转换结果: 01982fc0066e7000801373cdf1e15e27
往返转换正确: ✅
时间戳: 1753147770478 (2025-07-22T01:29:30.478Z)
序列号: 0
线程ID: 0
是否时间有序: ✅
测试 5:
直接生成(无横杠): 01982fc0066f700080137a721195d64a
对象转换(无横杠): 01982fc0066f7001801375f44ccd2494
原始UUID对象: 01982fc0-066f-7001-8013-75f44ccd2494
往返转换结果: 01982fc0066f700080137a721195d64a
往返转换正确: ✅
时间戳: 1753147770479 (2025-07-22T01:29:30.479Z)
序列号: 0
线程ID: 0
是否时间有序: ✅
=== 使用示例 ===
生成的UUID序列(不带横杠,注意时间有序性):
1. 01982fc00670700080137392533139cc
时间戳: 1753147770480 (2025-07-22T01:29:30.480Z)
序列号: 0
线程ID: 0
2. 01982fc0067c700080137ccd4309a364
时间戳: 1753147770492 (2025-07-22T01:29:30.492Z)
序列号: 0
线程ID: 0
3. 01982fc0067c700180137d5f20a5a1f6
时间戳: 1753147770492 (2025-07-22T01:29:30.492Z)
序列号: 1
线程ID: 0
4. 01982fc0067d700080137a4de52d4885
时间戳: 1753147770493 (2025-07-22T01:29:30.493Z)
序列号: 0
线程ID: 0
5. 01982fc0068c700080137282c22f92a1
时间戳: 1753147770508 (2025-07-22T01:29:30.508Z)
序列号: 0
线程ID: 0
6. 01982fc0068c7001801375346ea3ce33
时间戳: 1753147770508 (2025-07-22T01:29:30.508Z)
序列号: 1
线程ID: 0
7. 01982fc0068c70028013740394c1f233
时间戳: 1753147770508 (2025-07-22T01:29:30.508Z)
序列号: 2
线程ID: 0
8. 01982fc0069c700080137caa1dad0194
时间戳: 1753147770524 (2025-07-22T01:29:30.524Z)
序列号: 0
线程ID: 0
9. 01982fc0069c700180137987a493f43e
时间戳: 1753147770524 (2025-07-22T01:29:30.524Z)
序列号: 1
线程ID: 0
10. 01982fc0069c7002801377e4ae56f689
时间戳: 1753147770524 (2025-07-22T01:29:30.524Z)
序列号: 2
线程ID: 0
当前线程状态: 线程ID: 0, 上次时间戳: 1753147770524, 当前序列号: 2
=== 多线程使用示例 ===
线程 0 开始生成UUID:
线程 2 开始生成UUID:
线程 1 开始生成UUID:
线程2-1: 01982fc006ae700083137daf590d2e73 (线程ID: 12)
线程0-1: 01982fc006ae700082d370fd4c5d397d (线程ID: 11)
线程2-2: 01982fc006ae700183137ba74fa692ea (线程ID: 12)
线程1-1: 01982fc006ae7000835373643e76b263 (线程ID: 13)
线程2-3: 01982fc006af70008313775a1b1b60ca (线程ID: 12)
线程0-2: 01982fc006af700082d3722e433b19a3 (线程ID: 11)
线程2-4: 01982fc006af7001831374bcc72bb290 (线程ID: 12)
线程1-2: 01982fc006af700083537d9292c3b232 (线程ID: 13)
线程2-5: 01982fc006af70028313798d3a0e64ac (线程ID: 12)
线程0-3: 01982fc006af700182d3780ae8697d69 (线程ID: 11)
线程 2 状态: 线程ID: 12, 上次时间戳: 1753147770543, 当前序列号: 2
线程1-3: 01982fc006af7001835379875fbc16e1 (线程ID: 13)
线程1-4: 01982fc006b07000835373ef60ed1bae (线程ID: 13)
线程1-5: 01982fc006b0700183537c439bb9ad0b (线程ID: 13)
线程0-4: 01982fc006b0700082d373089376bc59 (线程ID: 11)
线程 1 状态: 线程ID: 13, 上次时间戳: 1753147770544, 当前序列号: 1
线程0-5: 01982fc006b0700182d376a4ffd2b2e8 (线程ID: 11)
线程 0 状态: 线程ID: 11, 上次时间戳: 1753147770544, 当前序列号: 1
✅ 总结一览
🔁 特性概括
测试维度 | 表现 | 说明 |
---|---|---|
唯一性 | ✅ 100% 唯一 | 多线程 / 高速场景下未发现冲突 |
性能 | ✅ 优异(5~6 倍) | 大幅优于标准 UUID v4 |
并发安全 | ✅ 无锁高并发 | ThreadLocal 隔离每线程状态 |
时间有序性 | ✅ 精准 | 同一毫秒内序列号递增,时间戳准确 |
可解析结构 | ✅ 可提取信息 | 支持反解时间戳、线程 ID、序列号 |
实用性 | ✅ 高 | 适用于日志追踪、ID 主键、链路分析等 |
可移植性 | ✅ 高 | Java 环境下无额外依赖,可直接集成使用 |
🔁 与标准 UUID 的差异对比
项目 | 标准 UUID v4 | 本工具类 UUID |
---|---|---|
格式 | 带横杠(36位) | 无横杠(32位) |
有序性 | ❌ 无顺序 | ✅ 时间有序 |
可解析信息 | ❌ 无法反解析 | ✅ 可提取时间戳、线程ID等 |
并发支持 | 有锁或线程不安全 | ✅ ThreadLocal 高并发友好 |
长度 | 36(含 - ) | 32(紧凑格式) |