高性能线程安全的时间有序 UUID 生成器 —— 基于 ThreadLocal 的实现
目录
- 🚀 高性能线程安全的时间有序 UUID 生成器 —— 基于 Java `ThreadLocal` 的实现
- 🔧 核心思路
- 🚀 高性能时间有序 UUID 生成器(无横杠)——Java 实现与详解
- ✅ 核心特性
- ✨ 核心设计理念
- 🧩 应用场景
- 📦 完整 Java 实现代码
- 🔍 关键功能说明
- ✅ 1. 无锁高并发:使用 ThreadLocal 管理线程状态
- ⏱️ 2. 时间戳 + 序列号控制顺序与唯一性
- 🧪 3. 内置测试方法
- 🚀 性能对比
- ✅ 总结一览
🚀 高性能线程安全的时间有序 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) {// 检查版本号是否为7return (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 v4startTime = 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();// 创建多个线程并发生成UUIDfor (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++) {// 生成UUIDString 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: 02. 01982fc0067c700080137ccd4309a364时间戳: 1753147770492 (2025-07-22T01:29:30.492Z)序列号: 0线程ID: 03. 01982fc0067c700180137d5f20a5a1f6时间戳: 1753147770492 (2025-07-22T01:29:30.492Z)序列号: 1线程ID: 04. 01982fc0067d700080137a4de52d4885时间戳: 1753147770493 (2025-07-22T01:29:30.493Z)序列号: 0线程ID: 05. 01982fc0068c700080137282c22f92a1时间戳: 1753147770508 (2025-07-22T01:29:30.508Z)序列号: 0线程ID: 06. 01982fc0068c7001801375346ea3ce33时间戳: 1753147770508 (2025-07-22T01:29:30.508Z)序列号: 1线程ID: 07. 01982fc0068c70028013740394c1f233时间戳: 1753147770508 (2025-07-22T01:29:30.508Z)序列号: 2线程ID: 08. 01982fc0069c700080137caa1dad0194时间戳: 1753147770524 (2025-07-22T01:29:30.524Z)序列号: 0线程ID: 09. 01982fc0069c700180137987a493f43e时间戳: 1753147770524 (2025-07-22T01:29:30.524Z)序列号: 1线程ID: 010. 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(紧凑格式) |