应优先使用ThreadLocalRandom处理高并发随机数,Random适用于需种子控制的场景,Math.random()仅适合临时脚本;SecureRandom专用于密码学,不可滥用。

Math.random() 只能生成 double 类型的 0.0 到 1.0(不含)随机数
它本质是调用 Random 单例的 nextDouble(),但封装得死死的——没提供种子控制、不能复现、也不能直接生成 int/long/boolean 等类型。你写 Math.random() * 10 得到的是 double,想转成 int 还得手动强转或 Math.floor(),一不留神就掉坑里。
常见错误现象:(int)(Math.random() * 10) 看似能生成 0–9,但实际范围是 [0, 9],而有人误以为能覆盖 10;更隐蔽的是,如果写成 (int)Math.random() * 10(括号错位),结果永远是 0。
- 适用场景:临时脚本、单元测试里快速凑个随机小数,不关心可重现性
- 性能影响:开销极小,但每次调用都同步访问内部静态
Random实例,在高并发下有轻微竞争 - 别指望它生成均匀分布的 long 或 boolean——它压根没提供这些方法
Random 类支持种子、多种类型、线程安全选择
Random 是真正的工具类,你可以 new 出多个实例,各自带独立种子,比如用固定种子做可复现的测试:new Random(12345L)。它直接提供 nextInt()、nextLong()、nextBoolean()、nextGaussian() 等,省去类型转换和边界计算。
使用场景:需要控制随机序列(如游戏关卡、A/B 测试分流)、批量生成整数、或要求线程隔离时。
立即学习“Java免费学习笔记(深入)”;
-
nextInt(int bound)生成 [0, bound) 的 int,比Math.random()手动缩放更安全、语义更清晰 - 多线程下,每个线程用自己
Random实例最稳妥;若共用,可用ThreadLocalRandom替代(见下一条) - 注意
setSeed(long)会重置内部状态,后续调用都从新种子开始——别在循环里反复 setSeed
高并发下优先用 ThreadLocalRandom 而不是 Random
ThreadLocalRandom 是为多线程优化的版本,每个线程持有一份私有 Random 实例,彻底避免锁竞争。它不支持构造器传种子,但提供了静态方法 current() 获取当前线程专属实例,再链式调用 nextInt() 等。
常见错误现象:在 Web 应用或并行流里仍用全局 Random 实例,吞吐量上不去,还可能因争抢导致随机性“变弱”(比如连续多次拿到相同值)。
- 必须用
ThreadLocalRandom.current().nextInt(1, 101)形式,不能 new —— 它的构造器是 private - 不支持自定义种子,所以不适合需要可重现性的测试场景
- 兼容性:JDK 7+,但 Android API Level 26+ 才完整支持(旧版可能抛
NoSuchMethodError)
SecureRandom 用于密码学场景,别乱用
SecureRandom 目标是抗预测、不可重现,底层依赖操作系统熵源(如 /dev/urandom),生成速度慢、初始化可能阻塞。它适合生成密钥、token、盐值等,但绝不用来填充数组或做游戏 RNG。
容易踩的坑:new SecureRandom() 在某些 Linux 环境下会卡住几秒——因为默认尝试读取 /dev/random(可能阻塞),建议显式指定算法:SecureRandom.getInstance("SHA1PRNG") 或用 getInstanceStrong()(JDK 8+)。
- 性能影响显著:比
Random慢 10 倍以上,别在高频循环里用 - 不要用它替代
Math.random()来“提高随机性”——这是典型误用 - 种子设置更严格:
setSeed(byte[])会混合原有熵,不是完全覆盖
真正要选哪个,取决于你手头这行代码跑在哪、是否要复现、有没有并发、以及“随机”到底对谁重要——业务逻辑里混用 Math.random() 和 Random 不少见,但一旦涉及测试一致性或高并发,漏掉 ThreadLocalRandom 或误用 SecureRandom 就很难回头。










