random高并发下卡住是因为多线程cas更新seed自旋竞争导致cpu空转;应改用每次调用threadlocalrandom.current().nextint(),禁用静态缓存、复用实例或synchronized包装。

为什么 Random 在高并发下会卡住
Random 的 nextInt() 看似线程安全,但底层靠 AtomicLong seed + CAS 自旋更新种子。当几十上百个线程同时调用,大部分线程会在 compareAndSet() 上失败重试,CPU 白耗在空转,吞吐量断崖下跌。
典型现象包括:JMC 或 Arthas 观察到大量线程处于 RUNNABLE 状态但实际没干活;压测时 QPS 上不去,ThreadLocalRandom.current().nextInt() 同等逻辑却能轻松翻倍。
- 不要复用同一个
Random实例跨线程使用 - 也不要为每个线程 new 一个
Random—— 虽然避开竞争,但构造开销大、种子初始值可能趋同(尤其系统时间精度不足时) - 更别用
synchronized(new Random()),锁粒度错、性能更差
ThreadLocalRandom.current() 必须每次调用,不能缓存静态实例
这是最容易踩的坑。有人图省事写成 private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();,结果所有线程共享同一个初始化时刻的种子状态,后续生成的随机数序列完全一致。
原因在于:ThreadLocalRandom.current() 不是简单返回单例,而是检查当前线程是否已初始化种子 —— 未初始化则调用 localInit()(混合 System.nanoTime()、threadId 等生成强隔离种子);已初始化才返回本线程专属实例。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- ✅ 正确写法:
ThreadLocalRandom.current().nextInt(100),每次需要时都调用current() - ❌ 错误写法:
private static final ThreadLocalRandom R = ThreadLocalRandom.current(); - ⚠️ 注意:在 ForkJoinPool 的子任务中同样适用,无需额外处理
边界参数行为与 Random 不完全兼容
ThreadLocalRandom 的 nextInt(int bound)、nextLong(long bound)、nextDouble(double bound) 都要求 bound > 0,否则抛 IllegalArgumentException;而 Random.nextInt(int bound) 同样如此,这点一致。
但注意区间语义:nextInt(50, 100) 表示 [50, 100),和 Random 完全相同;可一旦传入负数或 0,两者都立即失败,不静默降级。
- 不要假设
nextInt(-5)会返回 0 或抛其他异常 —— 它一定抛IllegalArgumentException - 并行流中直接用:
IntStream.range(0, 1000).parallel().map(i -> ThreadLocalRandom.current().nextInt(100)).toArray(),安全且无额外同步 - 若需复现某次随机序列(如测试),
ThreadLocalRandom不支持指定种子初始化,此时仍得用new Random(seed)+ 单线程上下文
ThreadLocalRandom 不是万能的,别在错误场景硬套
它解决的是「多线程高频调用随机数」这一具体问题。如果业务本身是单线程、或随机数调用频次极低(比如启动时生成一次密钥),用 Random 更直观,也无性能损失。
另外,它不提供 setSeed(long)、nextGaussian() 等方法 —— 这些能力被有意裁剪,因为线程局部场景下重置种子意义不大,而正态分布通常需更高精度控制,不在其设计目标内。
- 需要高斯分布?继续用
Random.nextGaussian(),或自己基于nextDouble()实现 Box-Muller - 需要可重现的测试随机流?坚持用
new Random(12345L),别试图给ThreadLocalRandom“塞种子” - 它的优势只在“每毫秒数百次以上、多线程争抢”的场景里真正体现出来,别为低频调用过度设计
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










