Random 的 seed 成为性能瓶颈是因为其 nextInt() 依赖全局 AtomicLong 并通过 CAS 更新,高并发下失败重试导致 CPU 空转;而 ThreadLocalRandom 每线程私有 seed,避免竞争,吞吐可达前者的 5 倍。

为什么 Random 的 seed 会成为性能瓶颈
Random 的 nextInt() 每次都要更新全局 AtomicLong seed,靠 CAS 实现线程安全。线程一多,seed.compareAndSet() 失败率飙升,失败后自旋重试——CPU 白跑,不是在算业务逻辑,是在抢“骰子”。实测 100 线程并发时,Random 的吞吐可能只有 ThreadLocalRandom 的 1/5。
ThreadLocalRandom.current() 必须在运行时调用,不能提前缓存
常见误用是把 ThreadLocalRandom.current() 写在 static 字段或类初始化块里:
private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current(); // ❌ 危险!
这会导致所有线程共享同一个初始实例(因为类加载时主线程调用的),彻底失去线程隔离意义,等同于退化为 Random。正确做法是:
- 只在真正执行业务逻辑的上下文中调用,比如
@RestController方法体、Runnable.run()、CompletableFuture回调内 - 每次需要随机数时都调用
ThreadLocalRandom.current().nextInt(...),不要保存返回值跨方法传递 - 禁止在
static {}或构造器中调用,否则可能触发NullPointerException(此时线程尚未完成初始化)
范围参数别写错:nextInt(origin, bound) 是左闭右开
ThreadLocalRandom.nextInt(1, 101) 表示 [1, 100] 的整数,语义清晰;而 nextInt(100) 是 [0, 100),容易引发边界错误。尤其在生成 ID、券码、分页偏移等场景,错一位就可能越界或重复。
立即学习“Java免费学习笔记(深入)”;
注意:origin 必须小于 bound,否则抛 IllegalArgumentException;且两个参数都必须是 int,不支持 long 范围(要用 nextLong(origin, bound))。
它不支持 setSeed(),也不提供 nextGaussian()
ThreadLocalRandom 压根没实现 setSeed(long),调用直接抛 UnsupportedOperationException。这不是缺陷,是设计取舍——它的种子由 System.nanoTime() 和线程 ID 衍生,不可控、不可重现。
如果你需要:
- 单元测试固定序列 → 用
new Random(12345L) - 正态分布随机数 → 自己套 Box-Muller 公式,或封装
Random.nextGaussian() - 无限随机流 →
Random.ints()可不设 size,ThreadLocalRandom.ints()必须指定streamSize或bound
最易被忽略的一点:它快,不是因为算法高级,而是因为每个线程玩自己的 seed——不争、不等、不卡。一旦你把它当全局对象用,优势瞬间归零。



















