threadlocalrandom.nextlong() 在高并发下比 random.nextlong() 快 2–5 倍,因无锁;单线程下 random 略快,因 threadlocalrandom 有线程局部查找开销;不可静态缓存实例,须每次调用 current() 或方法内缓存。

ThreadLocalRandom.nextLong() 比 Random.nextLong() 快多少?
在高并发循环中,ThreadLocalRandom 通常比 Random 快 2–5 倍,但这个差距只在竞争激烈时明显。单线程下两者几乎没差别,甚至 Random 略快——因为 ThreadLocalRandom 多了一层线程局部变量查找开销。
实操建议:
- 用 JMH 做基准测试时,必须开启多线程模式(如
@Fork(threads = 4)),否则测不出真实优势 - 避免在循环内反复调用
ThreadLocalRandom.current():它不轻量,应缓存引用 -
Random在多线程下会因AtomicLong.compareAndSet自旋争抢 seed,而ThreadLocalRandom每个线程独享 seed,无锁
为什么不能把 ThreadLocalRandom 当成全局单例用?
ThreadLocalRandom 不是线程安全的“工具类”,而是靠 ThreadLocal 绑定到当前线程的实例。直接声明 static final ThreadLocalRandom INSTANCE = ThreadLocalRandom.current() 是错的——它只会绑定到初始化时的那个线程,其他线程调用会触发 NullPointerException 或未定义行为。
常见错误现象:
立即学习“Java免费学习笔记(深入)”;
- 应用启动后前几秒正常,之后大量
NullPointerException,堆栈指向ThreadLocalRandom.nextLong - 在 Tomcat 或 Netty 的 worker 线程池中,随机数生成突然卡死或返回固定值
正确做法:
- 每次需要时调用
ThreadLocalRandom.current()(JDK 8+ 已优化为廉价操作) - 若频繁使用,可在方法入口缓存:
ThreadLocalRandom r = ThreadLocalRandom.current(); - 绝不保存
ThreadLocalRandom实例到静态字段、Spring Bean 或连接池对象中
nextDouble(0, 1) 和 nextDouble() 有性能差异吗?
有,而且不小。nextDouble()(无参)直接返回 [0.0, 1.0) 内的 double,底层用位运算拼接;而 nextDouble(double origin, double bound) 需要做范围校验、差值计算和浮点缩放,多出 2–3 次浮点运算和一次条件判断。
使用场景与取舍:
- 生成 [0, 100) 整数:用
nextInt(100),别用(int)(nextDouble() * 100) - 生成 [5.0, 15.0) 浮点:优先用
nextDouble(5.0, 15.0),语义清晰且 JDK 8+ 已内联优化 - 如果 bound 是变量(比如配置项),且该方法被高频调用,考虑预计算
bound - origin并缓存
Random 用 SecureRandom 替代是否更安全?
不推荐,除非你真在生成密钥或令牌。SecureRandom 是密码学强度的,但代价巨大:Linux 下可能阻塞读取 /dev/random,吞吐量常低于 Random 的 1%。
容易踩的坑:
- 在 HTTP 请求处理中用
SecureRandom生成 session ID,结果 QPS 从 5000 掉到 200 - 误以为“更随机=更安全”,但在非密码场景(如负载均衡选节点、模拟延迟),
ThreadLocalRandom完全够用 -
SecureRandom.getInstance("SHA1PRNG")在某些 JDK 版本下仍可能阻塞,且算法已被标记为 legacy
简单记:业务逻辑里所有“随机”需求,99% 场景用 ThreadLocalRandom;只有 generateKey()、createToken() 这类明确涉及密钥/凭证的,才查文档选对 SecureRandom 算法并预热。
真正难的是识别哪些地方“看起来像要安全,其实不需要”——比如 UUID.randomUUID() 底层用的就是 SecureRandom,但如果你只是用它做日志 traceId,那其实浪费了。











