对官方文档(https://openjdk.org/jeps/356)的不完全翻译,有自己的理解,如有错误请指正。本文的概括总结请看https://blog.csdn.net/weixin_38833041/article/details/125464166
摘要
为伪随机数生成器 (PRNG) 提供新的接口类型和实现,包括可跳转的 PRNG 和另一类可拆分的 PRNG 算法 (LXM)。
目标
- 更容易在应用程序中互换使用各种 PRNG 算法。
- 通过提供 PRNG 对象流更好地支持基于流的编程。
- 消除现有 PRNG 类中的代码重复。
- 保留 java.util.Random 类的现有行为。
非目标
提供许多其他 PRNG 算法的实现并不是目标,只是提供一个可以容纳其他 PRNG 算法的框架。但是,我们添加了三种已经在其他编程语言环境中广泛部署的常用算法。
成功指标
新 LXM 算法的输出通过了现有知名的 TestU01 和 PractRand 测试套件。
动机
我们专注于 Java 中伪随机数生成器领域的五个改进的领域:
- 对于传统的 PRNG 类 Random、ThreadLocalRandom 和 SplittableRandom,很难在应用程序中用其他算法替换其中的任何一个,尽管它们都支持几乎相同的一组方法。例如,如果应用程序使用 Random 类的实例,它必然声明 Random 类型的变量,该变量不能保存 SplittableRandom 类的实例;更改应用程序以使用 SplittableRandom 将需要更改用于保存 PRNG 对象的每个变量(包括方法参数)的类型。一个例外是 ThreadLocalRandom 是 Random 的子类,纯粹是为了允许 Random 类型的变量保存 ThreadLocalRandom 的实例,但 ThreadLocalRandom 几乎覆盖了 Random 的所有方法。接口可以很容易地解决这个问题。
- 传统的 Random、ThreadLocalRandom 和 SplittableRandom 都支持诸如 nextDouble() 和 nextBoolean() 之类的方法以及诸如 ints() 和 longs() 之类的流生成方法,但它们具有完全独立且几乎复制和粘贴的相同的方法实现。重构此代码将使维护更容易,此外,文档将使第三方更容易创建新的 PRNG 类,这些类也支持相同的完整方法套件。
- 2016 年,测试揭示了 SplittableRandom 类使用的算法中的两个新弱点。一方面,相对较小的修订可以避免这些弱点。另一方面,还发现了一类新的可拆分 PRNG 算法 (LXM),它几乎一样快,甚至更容易实现,并且似乎完全避免了 SplittableRandom 容易出现的三类弱点。
- 能够从 PRNG 获取 PRNG 对象流使得使用流式方法表达某些类型的代码变得更加容易。
- 文献中有许多不可拆分但可跳转的 PRNG 算法(也许也是可跳转的,即能够进行非常长的跳转和普通跳转),这一属性与拆分完全不同,但也适用于支持流PRNG 对象。过去,很难在 Java 中利用此属性。可跳转 PRNG 算法的示例是 Xoshiro256** 和 Xoroshiro128+。
描述
我们提供了一个新接口 RandomGenerator,它为所有现有的和新的 PRNG 提供了一个统一的 API。 RandomGenerators 提供名为 ints、longs、doubles、nextBoolean、nextInt、nextLong、nextDouble 和 nextFloat 的方法,以及它们当前的所有参数的变化。
我们提供了四个新的专用 RandomGenerator 接口:
- SplittableRandomGenerator 扩展了 RandomGenerator 并且还提供
名为 split 和 splits 的方法。 可拆分性允许用户从现有的 RandomGenerator 生成一个新的 RandomGenerator,这通常会产生统计上独立的结果。 - JumpableRandomGenerator 扩展了RandomGenerator 并且还提供
名为 jump 和 jumps 的方法。 可跳跃性允许用户跳到中等数量的抽签。 - LeapableRandomGenerator 扩展了 RandomGenerator 并且还提供
方法名为leap和leaps的方法。 可跳跃性允许用户跳过大量的抽签。 - ArbitrarilyJumpableRandomGenerator 扩展了 LeapableRandomGenerator 并且还提供了jump 和 jumps 的方法额外的变体,允许指定任意跳跃距离。
我们提供了一个新类 RandomGeneratorFactory,它用于定位和构造 RandomGenerator 实现的实例。 RandomGeneratorFactory 使用 ServiceLoader.Provider API 来注册 RandomGenerator 的实现。
我们重构了 Random、ThreadLocalRandom 和 SplittableRandom,以便共享它们的大部分实现代码,此外,这些代码也可以被其他算法重用。此重构创建底层非公共抽象类 AbstractRandomGenerator、AbstractSplittableRandomGenerator、AbstractJumpableRandomGenerator、AbstractLeapableRandomGenerator 和 AbstractArbitrarilyJumpableRandomGenerator,每个只提供如下方法的实现: nextInt()、nextLong() (如果相关)还有split( ) 或 jump( ) ,或jump( ) 和leap( ) ,或jump(distance)。经过这次重构,Random、ThreadLocalRandom、SplittableRandom 继承了 RandomGenerator 接口。请注意,因为 SecureRandom 是 Random 的子类,所以 SecureRandom 的所有实例也自动支持 RandomGenerator 接口,无需重新编码 SecureRandom 类或其任何相关的实现引擎。
我们还添加了扩展 AbstractSplittableRandomGenerator(并因此实现 SplittableRandomGenerator 和 RandomGenerator)的底层非公共类,以支持 LXM 系列 PRNG 算法的六个特定成员:
- L32X64MixRandom
- L32X64StarStarRandom
- L64X128MixRandom
- L64X128StarStarRandom
- L64X256MixRandom
- L64X1024MixRandom
- L128X128MixRandom
- L128X256MixRandom
- L128X1024MixRandom
我们还提供了这些广泛使用的 PRNG 算法的实现:
- Xoshiro256PlusPlus
- Xoroshiro128PlusPlus
上面提到的非公共抽象实现将来可能作为随机数实现器 SPI 的一部分提供。
这套算法为 Java 程序员在空间、时间、质量和与其他语言的兼容性之间提供了合理的权衡范围。
备选方案
我们考虑简单地引入新接口,同时保留 Random、ThreadLocalRandom 和 SplittableRandom 的实现。这将有助于使 PRNG 对象更容易互换,但不会更容易实现它们。
我们考虑在不更改其功能或添加任何新接口的情况下重构 Random、ThreadLocalRandom 和 SplittableRandom。我们相信这会减少他们的整体内存占用,但不会让未来的 PRNG 算法更容易实现或使用。
风险和假设
我们认为这是一个中等项目,风险很小。大概最大的负担是制定规范,第二大负担是测试。
已注意确保传统随机数生成器的行为不受影响。