在Java中如何使用随机数_JavaRandom类操作说明

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 每个线程独享内部状态,避免了 RandomAtomicLong 的 CAS 重试开销。但它不能指定种子,且只能通过静态方法获取:

  • 不能用 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 或需要可分割的随机源(比如分片生成独立随机数列),SplittableRandomRandom 更合适。它的 split() 方法能快速派生出统计独立的新实例。

  • split() 出的实例与原实例无相关性,适合工作窃取场景
  • 不兼容旧版 JDK(仅 8u121+ 及 JDK 9+)
  • 不提供 nextGaussian() 等高级分布方法

种子不可控、不可复现这点,在压测或调试时容易被忽视。