Java中正确使用随机数需选用java.util.Random、ThreadLocalRandom或SplittableRandom;单线程用new Random(),多线程高频场景优先ThreadLocalRandom.current();nextInt(n)生成[0,n)整数,闭区间[a,b]应写为nextInt(b-a+1)+a。

Java里没有 Random 类的别名叫“Random\_”,也没有内置的 JavaRandom 类——这是常见误解。真正可用的是标准库中的 java.util.Random,以及更现代、线程安全的 java.util.concurrent.ThreadLocalRandom 和函数式友好的 java.util.SplittableRandom。
如何正确创建和使用 Random 实例
Random 是一个普通类,必须显式实例化。不建议在高并发场景下共享单个实例(虽线程安全但有竞争开销),也不推荐每次用都新建(影响性能)。
- 单线程或低频使用:直接
new Random(),种子默认为当前时间纳秒 - 需要可重现序列:传入固定
long种子,如new Random(42L) - 多线程高频调用:改用
ThreadLocalRandom.current(),它不依赖共享状态
Random rand = new Random(); int i = rand.nextInt(10); // [0, 10) double d = rand.nextDouble(); // [0.0, 1.0)
nextInt()、nextLong() 等方法的范围陷阱
这些方法默认返回“非负”值,但关键在于:它们的参数表示**上界(exclusive)**,不是范围长度。比如 nextInt(5) 返回的是 0,1,2,3,4,不是 -5 ~ +5。
-
nextInt(n)→[0, n),n 必须 > 0,否则抛IllegalArgumentException - 要生成
[a, b]闭区间整数:用rand.nextInt(b - a + 1) + a -
nextDouble()和nextFloat()永远只返回[0.0, 1.0),不能传参
为什么 ThreadLocalRandom 在并发循环中更快
ThreadLocalRandom 每个线程独享内部状态,避免了 Random 中 AtomicLong 的 CAS 重试开销。但它不能指定种子,且只能通过静态方法获取:
立即学习“Java免费学习笔记(深入)”;
- 不能用
new ThreadLocalRandom()—— 构造私有,必须调用current() - 不支持
setSeed(long),所以无法复现随机序列 - 在 for 循环内反复调用
ThreadLocalRandom.current().nextInt(100)是安全且高效的
for (int i = 0; i < 1000; i++) {
int r = ThreadLocalRandom.current().nextInt(1, 101); // [1, 101)
}别忽略 SplittableRandom 的适用场景
如果你在做并行流(parallelStream())、ForkJoin 或需要可分割的随机源(比如分片生成独立随机数列),SplittableRandom 比 Random 更合适。它的 split() 方法能快速派生出统计独立的新实例。
-
split()出的实例与原实例无相关性,适合工作窃取场景 - 不兼容旧版 JDK(仅 8u121+ 及 JDK 9+)
- 不提供
nextGaussian()等高级分布方法
种子不可控、不可复现这点,在压测或调试时容易被忽视。










