多线程场景必须用ThreadLocalRandom,单线程可用Random;前者线程隔离避免CAS竞争,吞吐量高3–5倍,但不支持setSeed和nextGaussian,且须用current()获取实例而非静态缓存。

Java里用Random还是ThreadLocalRandom,取决于你是不是在多线程环境里生成随机数——单线程用Random足够,高并发场景必须换ThreadLocalRandom,否则会因竞争导致性能明显下降。
什么时候该用 ThreadLocalRandom 而不是 Random
ThreadLocalRandom专为多线程设计,每个线程持有独立实例,避免了Random内部AtomicLong的CAS争用。实际压测中,100+线程并发调用nextInt()时,ThreadLocalRandom吞吐量通常是Random的3–5倍。
- Web服务中每个请求生成订单号、验证码、采样ID等,用
ThreadLocalRandom.current() - 定时任务批量处理数据时需要随机打散顺序,且任务本身被多个线程触发
- 使用
ForkJoinPool或CompletableFuture并行流时,不要在lambda里共享同一个Random实例 - 注意:
ThreadLocalRandom不能通过new构造,必须调用current()获取当前线程专属实例
Random 和 ThreadLocalRandom 的常用方法差异
两者API高度兼容,但ThreadLocalRandom不支持设置种子(setSeed(long)),也没有无参构造函数;它也不提供nextGaussian()这类非均匀分布方法。
- 共同可用:
nextInt()、nextInt(int bound)、nextLong()、nextDouble()、nextBoolean() -
Random特有:setSeed(long)(用于可重现随机序列)、nextBytes(byte[])、nextGaussian() -
ThreadLocalRandom特有:current()(唯一入口)、nextInt(int origin, int bound)(带范围上下界) - 生成[1, 100]之间的随机整数:
ThreadLocalRandom.current().nextInt(1, 101),注意右边界是开区间
常见错误:混用或误初始化
最常踩的坑是把ThreadLocalRandom当普通工具类缓存成静态字段,或者在非线程安全上下文中复用Random实例。
立即学习“Java免费学习笔记(深入)”;
- ❌ 错误写法:
private static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current();—— 这会绑定到类加载时的线程,后续其他线程调用会出错 - ❌ 错误写法:
private static final Random RANDOM = new Random();在Spring Bean里被多个HTTP线程共用 - ✅ 正确写法:每次需要时调用
ThreadLocalRandom.current().nextInt(100),无需缓存 - ✅ 如果确实需要可重现的随机序列(如单元测试),才用
new Random(12345L),并确保实例不跨线程共享
public class RandomUsageExample {
// ✅ 正确:每次调用都获取当前线程专属实例
public int generateId() {
return ThreadLocalRandom.current().nextInt(1_000_000, 9_999_999);
}
// ✅ 正确:单线程/低并发场景下用 Random 也完全OK
private final Random legacyRandom = new Random(System.nanoTime());
public double legacySample() {
return legacyRandom.nextDouble();
}
}
真正容易被忽略的是:ThreadLocalRandom的current()方法在未初始化的线程(比如刚创建还没执行过任何ThreadLocalRandom操作的线程)中首次调用会有微小开销,但这远小于锁竞争成本;另外,它不支持自定义随机算法,所有分布都基于LHS(Linear Congruential Generator)变种,对密码学安全场景完全不适用——那种情况得用SecureRandom。










