当前位置: 首页 > news >正文

高性能线程安全的时间有序 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(低位)2Variant(固定)
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);
  • 每个线程拥有独立的 lastTimestampsequence,避免竞争和锁开销。
  • 线程 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(紧凑格式)
http://www.dtcms.com/a/292792.html

相关文章:

  • 实操:AWS CloudFront的动态图像转换
  • Cadence 原理图如何给网络名称添加页码
  • 『React』条件渲染的7种方法
  • 基于Prompt 的DevOps 与终端重塑
  • 装备数字孪生底座平台探索
  • HTTP 协议常见字段(请求头/响应头)
  • 虚幻5入门教程:如何在虚幻5中创建一个C++的Plugin
  • android 12 的 aidl for HAL 开发示例
  • VR技术在元宇宙游戏中的作用及发展前景深度分析
  • Fluent许可与网络安全策略
  • 基于JSP的快递管理系统/快递寄出管理系统
  • 初识opencv01——基本api操作
  • Transactional事务不生效案例与解决方案?
  • 基于深度学习Transform的steam游戏特征分析与可视化【词云-情感词典分析-主题分析-词频分析-关联分析】
  • Android 自动的文件管理器存在以下问题,文件管理器横竖屏切换会停止运行,如何解决
  • 多维基分析求导法则
  • DNS应用层协议
  • OEC 刷机Armbain 25.05后配置说明
  • Vue3中watch和watchEffect区别和用法
  • 安装UV(一个python工具)
  • C语言符号可见性控制与工程实践——深入理解 __attribute__((visibility)) 和 -fvisibility=hidden
  • 【bug】Yolo11在使用tensorrt推理numpy报错
  • 在线教育培训课程视频如何防下载、防盗录?
  • Java(LinkedList和ArrayList底层分析)
  • UML中的多重性详解
  • 【QT常用技术讲解】QSystemTrayIcon系统托盘
  • cuda做lut 去畸变示例
  • APT32F1732RBT8爱普特微电子 32位MCU国产芯片 智能家居/工业控制 首选
  • 【Word Press基础】创建一个自定义区块
  • 日语学习-日语知识点小记-构建基础-JLPT-N3阶段(7):自動詞 & 他動詞