java 中生成随机数是日常开发中最常见的需求之一:从简单的抽奖、验证码,到游戏、模拟、测试数据,再到安全敏感的密钥、token 生成,随机数无处不在。
但很多人只知道 math.random(),其实 java 提供了多种随机数生成方式,每种适用场景完全不同,用错会导致性能爆炸、线程安全问题,甚至安全漏洞!
本文从零基础讲到精通,带你彻底搞懂 java 随机数的全部玩法。
一、java 随机数家族全景图(推荐收藏)
| 方式 | 包路径 | 随机类型 | 线程安全 | 性能(并发下) | 安全性 | 推荐场景 | jdk 版本 |
|---|---|---|---|---|---|---|---|
| math.random() | java.lang | 伪随机 | 是 | 中等 | 低 | 非常简单的单线程场景 | 1.0 |
| java.util.random | java.util | 伪随机 | 是(但慢) | 差 | 低 | 单线程或低并发普通随机 | 1.0 |
| threadlocalrandom | java.util.concurrent | 伪随机 | 每个线程独立 | 极高 | 低 | 高并发普通随机(目前最推荐的普通场景) | 1.7 |
| securerandom | java.security | 加密安全随机 | 是 | 较低 | 高(加密级) | 密码、密钥、token、nonce、验证码等安全场景 | 1.1 |
| splittablerandom | java.util | 伪随机 | 非线程安全 | 高 | 低 | 并行计算、流式处理(forkjoinpool 等) | 1.8 |
二、零基础快速上手(最常用的几种写法)
1. 最简单的方式(入门必会)
// [0.0, 1.0) 之间的 double double d = math.random(); // 0 ~ 9 的整数 int num = (int) (math.random() * 10); // 更推荐:使用 random(范围更清晰) random random = new random(); int num2 = random.nextint(10); // [0, 10) int num3 = random.nextint(5, 15); // [5, 15) jdk 17+ 才支持
2. 生成指定范围的整数(最常用写法)
// 通用写法:min(包含) ~ max(不包含)
public static int nextint(int min, int max) {
return min + new random().nextint(max - min);
}
// 更高效(推荐)
threadlocalrandom.current().nextint(min, max);
三、三大主流随机数生成器详细对比与源码级讲解
1. random(最经典,但别乱用)
random r = new random(); // 默认种子 = 当前时间毫秒 // 或 random r = new random(123456789l); // 指定种子 → 可复现 r.nextint(); // int 全范围 r.nextint(100); // [0,100) r.nextboolean(); r.nextdouble(); // [0.0, 1.0) r.nextgaussian(); // 高斯分布(正态分布)
缺点:
- 多线程下内部用 cas + synchronized,竞争激烈时性能很差
- 种子可预测(尤其是用时间戳),安全性低
2. threadlocalrandom(高并发首选,性能之王)
// 正确用法(不要 new) threadlocalrandom random = threadlocalrandom.current(); // 用法和 random 几乎一样 int i = random.nextint(100); int range = random.nextint(10, 50); // [10,50) long l = random.nextlong(1_000_000); double d = random.nextdouble(0.0, 1.0);
为什么比 random 快很多?
- 每个线程拥有独立的随机数生成器(threadlocal 思想)
- 无锁、无竞争
- 官方推荐:在高并发场景下替换 random,性能可提升 3~10 倍
注意:不支持手动设置种子(设计上故意禁止),不可复现。
3. securerandom(安全场景必须用)
// 最常用写法
securerandom sr = new securerandom(); // 默认强随机
// 指定算法(常见)
securerandom sr2 = securerandom.getinstance("nativeprng");
securerandom sr3 = securerandom.getinstancestrong(); // 最安全(但最慢)
byte[] token = new byte[32];
sr.nextbytes(token); // 填充 32 字节安全随机数
int code = 100_000 + sr.nextint(900_000); // 6位验证码
特点:
- 种子来自操作系统熵池(鼠标移动、键盘敲击、硬件中断等)
- 不可预测,不可复现
- 用于:session id、csrf token、盐、密钥、uuid v4 等
性能:比 random 慢 10~100 倍,但安全第一。
四、常见场景最佳实践(直接抄就行)
普通游戏、测试数据、模拟、抽奖
→ threadlocalrandom.current()
web 后台普通随机(日志、分配 id 等)
→ threadlocalrandom
验证码、短信码、找回密码 token
→ securerandom
生成安全密钥、nonce、签名盐
→ securerandom.getinstancestrong() 或 nativeprng
想要结果可复现(调试、测试用例)
→ new random(固定种子)
并行流或 forkjoinpool 大规模并行计算
→ splittablerandom
五、经典误区与面试高频问题
- math.random() 底层就是 new random(),多线程下性能差
- random 是线程安全的,但并发性能极差
- threadlocalrandom 不能 new,只能用 current()
- securerandom 不要在循环里反复 new(很慢),复用一个实例
- 永远不要用 random 生成密码/密钥/验证码(可被预测攻击)
- jdk 17+ 推荐:randomgenerator 接口统一 api(未来趋势)
六、终极总结一句话
- 普通随机 → threadlocalrandom(性能最高)
- 安全随机 → securerandom(安全第一)
- 想要可复现 → new random(种子)
- 简单写 → math.random()(但别在高并发用)
掌握这三大家族,基本覆盖 99% 的随机数需求。
到此这篇关于java中随机数生成的多种方式的文章就介绍到这了,更多相关java随机数生成方式内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论