我几乎同时在两个线程上初始化两个随机数生成器,我希望这两个生成器的行为完全不同。我会Random.nextInt(7)
经常一个接一个地调用两个生成器。使用System.currentTimeMillis()
不是一个好主意,因为看起来我的计算机速度非常快,以至于我从两个生成器获得的数字很有可能是相同的。那么有没有办法配置它们,Random
以便尽管它们被一个接一个地调用,但它们的行为仍然不同?我希望解决方案是跨平台兼容的,因此任何特定于平台的想法(例如读取)/dev/random
都是不可接受的。感谢帮助。
5 回答
一种方法:使用 UUID (GUID) 为每个 Random 实例播种,该 UUID (GUID) 转换为 long。
其他答案建议使用nanoTime,这可能取决于硬件的速度,但我更喜欢 UUID 路由。
您可以使用System.nanoTime()
which 将减少两个线程具有相同种子的可能性。
或者创建一个线程安全的实用程序类来为您获取随机数对象,同时确保它为每个新实例使用不同的种子。
此外,您可以为每个线程分配一个数字 id作为线程名称并将其附加到种子
您可以使用SecureRandom
为您的两个随机数生成器生成种子。它使用可以使用特定于平台的熵源的服务提供商基础架构,因此在可用的系统上,您可能会从中获得熵,/dev/random
而不必担心代码中的熵。另一个答案中提到的UUID是从这样的来源生成的,至少在 OpenJDK 7.6 中是这样。
也就是说,请注意,从 Java 7 开始,在多线程中使用 PRNG 的首选方式是ThreadLocalRandom
. 我不确定,但在我看来,这里的主要目标是避免同步开销,而不是播种问题。至少在 OpenJDK 7.6 中,构造函数使用默认Random
构造函数,而默认构造函数又使用高分辨率系统时间(以纳秒为单位,但不一定具有实际分辨率),并结合静态变量的当前值。后者即使对于在系统时钟的同一滴答声中创建的实例也能确保不同的种子,因此即使Random
使用默认构造函数简单地构造实例,您的原始问题也应该消失。
这个唯一性是在 2010 年引入的,以响应错误报告 #6937857。该报告是针对 Java 7 报告的,并且在 Java 7 中也已修复,但根据 mercurial 存储库,此更改应该首先包含在jdk7-b94
release中。
您可以使用System.nanoTime()
, 而不是System.currentTimeMillis()
.
只需将第二个线程的创建延迟 30 毫秒,这将导致 nanoTime() 与 thread1 的 nanoTime() 随机不同
所以你的问题有一个实际的观点,对于这个观点 nanoTime() 我认为足够了。
另一种观点是军事安全加密的更理论化的观点。在那里,您想使用使用物理随机性(随机寄存器)的特殊硬件。但这不是你的意图。
不要做任何事。有用!
这是 java.util.Random 的代码:
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
顾名思义,这seedUniquifier
使种子独一无二。它适用于单线程和多线程:
private static long seedUniquifier() {
// L'Ecuyer, "Tables of Linear Congruential Generators of
// Different Sizes and Good Lattice Structure", 1999
for (;;) {
long current = seedUniquifier.get();
long next = current * 181783497276652981L;
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}