高性能线程安全的时间有序 UUID 生成器 —— 基于 ThreadLocal 的实现

🚀 高性能线程安全的时间有序 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) {
        // 检查版本号是否为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);
  • 每个线程拥有独立的 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: 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(紧凑格式)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值