1. 概述
Java SE 17 引入了随机数生成 API 的重大更新——JEP 356。这次更新带来了三大核心改进:
- 新增了多种接口类型
- 提供了便捷的生成器工厂查找/实例化方法
- 引入了一组全新的随机数生成器实现
本文将对比新旧 API 的差异,演示如何查找和选择生成器,并分析线程安全性与性能表现。
2. 传统 Random API
2.1. API 设计缺陷
传统 API 存在明显的设计短板:完全基于类实现,缺乏接口抽象。核心类包括:
graph TD
A[Random] --> B[ThreadLocalRandom]
A --> C[SecureRandom]
A --> D[SplittableRandom]
2.2. Random 基础用法
最常用的 java.util.Random
使用方式简单粗暴:
Random random = new Random();
int number = random.nextInt(10);
assertThat(number).isPositive().isLessThan(10);
默认构造函数会自动设置种子值,保证每次调用生成的随机数序列不同。
2.3. 替代方案
针对不同场景,Java 提供了三种替代方案:
类名 | 适用场景 | 核心优势 |
---|---|---|
ThreadLocalRandom |
多线程环境 | 避免竞争开销 |
SecureRandom |
加密场景 | 密码学安全 |
SplittableRandom |
并行计算 | 优化 fork/join 性能 |
3. 新 RandomGenerator API
3.1. 架构升级
新 API 通过接口重构解决了传统设计的缺陷:
graph TD
I[RandomGenerator] --> J[SplitableGenerator]
I --> K[JumpableGenerator]
I --> L[LeapableGenerator]
I --> M[ArbitrarilyJumpableGenerator]
J --> N[Xoroshiro128PlusPlus]
J --> O[Xoshiro256PlusPlus]
K --> P[L128X1024MixRandom]
K --> Q[L128X128MixRandom]
3.2. 核心改进
传统 API 的两大痛点在新版本中得到了彻底解决:
- 算法切换困难:缺乏接口导致实现耦合
- 代码重复严重:
SplittableRandom
与Random
存在大量重复代码
新 API 的四大设计目标:
- 实现算法无缝切换
- 增强流式编程支持
- 消除冗余代码
- 保持向后兼容性
3.3. 新增接口
RandomGenerator
作为根接口提供了统一 API,并扩展出四个专业接口:
SplitableGenerator
:支持生成派生生成器JumpableGenerator
:支持中等步长跳跃LeapableGenerator
:支持大步长跳跃ArbitrarilyJumpableGenerator
:支持自定义跳跃距离
4. RandomGeneratorFactory 工厂类
4.1. 遍历所有生成器
通过 all()
方法可获取所有可用生成器工厂:
RandomGeneratorFactory.all()
.sorted(Comparator.comparing(RandomGeneratorFactory::name))
.forEach(factory -> System.out.println(String.format("%s\t%s\t%s\t%s",
factory.group(),
factory.name(),
factory.isJumpable(),
factory.isSplittable())));
工厂发现机制基于 Service Provider API,自动扫描
RandomGenerator
接口实现。
4.2. 按属性筛选
结合 Stream API 可按需筛选生成器:
RandomGeneratorFactory.all()
.filter(RandomGeneratorFactory::isJumpable)
.findAny()
.map(RandomGeneratorFactory::create)
.orElseThrow(() -> new RuntimeException("生成器创建失败"));
5. 生成器选择策略
5.1. 默认生成器
无特殊需求时,直接使用默认生成器最简单:
RandomGenerator generator = RandomGenerator.getDefault();
当前默认实现为
L32X64MixRandom
,但未来版本可能变更。
5.2. 指定算法
需要特定算法时,通过名称精确选择:
RandomGenerator generator = RandomGenerator.of("L128X256MixRandom");
若算法不存在会抛出
IllegalArgumentException
,建议先校验可用性。
6. 线程安全处理
新生成器大多非线程安全(除 Random
/SecureRandom
外),多线程场景有两种处理方式:
方案一:共享线程安全生成器
// 使用 SecureRandom 替代
RandomGenerator secureGen = RandomGenerator.of("SecureRandom");
方案二:派生独立生成器
List<Integer> numbers = Collections.synchronizedList(new ArrayList<>());
ExecutorService executor = Executors.newCachedThreadPool();
RandomGenerator.SplittableGenerator sourceGen = RandomGeneratorFactory
.<RandomGenerator.SplittableGenerator>of("L128X256MixRandom")
.create();
sourceGen.splits(20).forEach(splitGen ->
executor.submit(() -> numbers.add(splitGen.nextInt(10)))
);
派生方式能避免竞争,且保证各线程随机序列独立。
7. 性能对比
基于 Java 17 的基准测试(生成 4 种类型随机数):
private static void generateRandomNumbers(RandomGenerator generator) {
generator.nextLong();
generator.nextInt();
generator.nextFloat();
generator.nextDouble();
}
测试结果(单位:纳秒/操作):
算法 | 耗时 | 特点 |
---|---|---|
SecureRandom | 9,457,881 | 密码学安全,但性能最差 |
Random | 111,369 | 传统实现,线程安全 |
SplittableRandom | 27,753 | 并行优化最优 |
L64X128StarStarRandom | 34,054 | 新生成器性能代表 |
新生成器因无需线程安全开销,性能普遍优于
Random
。
8. 总结
Java 17 的随机数生成更新带来三大核心价值:
- 架构升级:接口化设计实现算法解耦
- 使用便捷:工厂模式简化生成器选择
- 性能优化:新算法吞吐量显著提升
完整示例代码请参考 GitHub 仓库。