threadlocalrandom通过为每个线程分配独立种子彻底消除cas竞争,吞吐量可达random的5倍以上;它不共享状态、无同步开销,但禁止静态缓存实例、须用双参范围方法、不支持可复现序列。

直接用 ThreadLocalRandom.current() 替代共享的 Random 实例,就能绕开原子种子竞争——它不抢同一个“骰子”,而是让每个线程各玩各的。
为什么 Random 会卡在高并发里
Random 的 nextInt() 每次都要更新一个全局的 AtomicLong seed,靠 CAS 操作保证线程安全。线程一多,大家同时去改这个 seed,失败就自旋重试,CPU 白转、不产数、只耗电。实测 100 线程下,CAS 失败率常超 40%,吞吐可能不到 ThreadLocalRandom 的 1/5。
ThreadLocalRandom 是怎么破局的
它根本不共享状态:
- 首次调用
current()时,基于系统纳秒时间、线程 ID 等混合生成专属种子,存进当前线程对象的私有字段(如threadLocalRandomSeed) - 后续所有
nextInt()、nextLong()都只读写本线程内存,无 CAS、无锁、无同步 - 算法本身是优化过的线性同余式,计算全在寄存器完成,缓存命中率高达 98%
必须遵守的三条使用铁律
错一条,就可能退化回 Random:
-
禁止 static 缓存:比如
private static final ThreadLocalRandom R = ThreadLocalRandom.current();—— 类加载时主线程初始化的实例,其他线程拿到的是未就绪或复用状态,等于白换 -
范围优先用双参形式:写
nextInt(1, 101)(生成 [1, 100]),别写nextInt(100) + 1;前者语义清、边界稳、不越界 -
高频路径可提局部变量:在循环或密集计算中,先写
ThreadLocalRandom r = ThreadLocalRandom.current();,再反复调r.nextInt(),避免多次方法调用开销
什么场景适合,什么场景要绕开
它不是万能钥匙:
- 适合:发券码、抽奖、ID 偏移、压测数据生成、ForkJoinPool 任务分片——只要求“够随机”,不要求“可复现”
- 不适合:需要固定 seed 调试算法、跨线程协同生成同一随机序列(如分布式哈希)、游戏回放逻辑——此时仍得用
new Random(seed)或SecureRandom










