ThreadLocalRandom 通过将种子直接写入 Thread 对象私有字段并用 Unsafe 操作内存偏移量,避免 CAS 自旋;不依赖 ThreadLocal,无内存泄漏与哈希开销;需每次调用 current() 保证线程隔离;seed 不可设置,牺牲可重现性换取并发性能。

ThreadLocalRandom 怎么绕过 CAS 自旋锁
它根本没用 CAS。ThreadLocalRandom 不共享 seed 变量,而是把每个线程的种子直接写进 Thread 对象的私有字段里(比如 threadLocalRandomSeed),靠 Unsafe.putLong(t, SEED, value) 直接操作内存偏移量。没有“读-改-写”循环,自然没有自旋失败、没有 CPU 白耗。
对比 Random.next() 里那个经典的 do-while CAS 循环:
– 多个线程抢同一个 AtomicLong seed
– 一次成功,其余全失败重试
– 线程数一上 8,自旋开销就明显吃掉 CPU
internal 隔离性不是 ThreadLocal 实现的
名字带 ThreadLocal 容易让人误以为它内部用了 ThreadLocal,其实完全没用。源码里找不到 ThreadLocal 字段,只有 Unsafe 对 Thread 实例字段的直接读写。这意味着:
- 不依赖
ThreadLocal的哈希表结构,避免了潜在的内存泄漏风险 - 不走
ThreadLocalMap的 get/set 流程,少了哈希计算和扩容逻辑 - 种子存储位置紧贴
Thread对象头,对 CPU 缓存行更友好(减少伪共享)
为什么不能提前缓存 current() 结果
常见误写:private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();
这行代码在类加载时执行,所有线程共用初始化那一刻的单个实例——等于又退化回共享 seed 模式。真正起效的前提是每次调用都走 current():
-
current()内部会检查当前线程是否已初始化 seed,未初始化则生成并写入 - 后续调用直接读取本线程专属字段,全程无跨线程交互
- 哪怕你在
ForkJoinPool或虚拟线程里用,也自动适配新线程生命周期
隔离性带来的副作用:不可控 seed
ThreadLocalRandom 的种子由系统安全随机源初始化,且 setSeed(long) 被重写为直接抛 UnsupportedOperationException。这意味着:
如果你需要复现某次压测中的随机行为(比如调试抽奖逻辑),它就不适用;必须换回 new Random(12345L) 这种带固定 seed 的方式。
它的隔离优势,本质是用“不可重现性”换来的——各线程种子独立演化,彼此绝缘,也绝缘于外部控制。










