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 的两大痛点在新版本中得到了彻底解决:

  1. 算法切换困难:缺乏接口导致实现耦合
  2. 代码重复严重SplittableRandomRandom 存在大量重复代码

新 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 的随机数生成更新带来三大核心价值:

  1. 架构升级:接口化设计实现算法解耦
  2. 使用便捷:工厂模式简化生成器选择
  3. 性能优化:新算法吞吐量显著提升

完整示例代码请参考 GitHub 仓库


原始标题:Random Number Generators in Java 17