关于java:使用当前时间与无时间的随机数生成器

Using Random Number Generator with Current Time vs Without

我想了解使用一个以System.currentTimeMillis()作为种子的随机数生成器和只使用默认构造函数之间的区别。也就是说,这两者的区别是什么:

1
Random rand = new Random(System.currentTimeMillis());

还有这个:

1
Random rand = new Random();

我知道这些数字是伪随机的,但是我还没有完全理解细节,以及它们是如何在"随机性"级别之间发生的,当当前时间被用作种子时,以及当使用默认构造函数时。


提供自己的种子对于有意多次生成相同伪随机值序列的模拟非常有用。不过,一般来说,使用默认的构造函数也是一样的。

当使用默认构造函数时,文档会说:

This constructor sets the seed of the random number generator to a value very likely to be distinct from any other invocation of this constructor.

换句话说,它在内部产生自己的种子。细节取决于所使用的特定Java实现。我看到的一个实现是这样的:

1
2
3
4
5
6
private static volatile long seedBase = 0;

public Random() {
    setSeed(System.nanoTime() + seedBase);
    ++seedBase;
}

随机性的实际质量并没有改变。如果您关心随机序列的质量,也可以使用java.security.SecureRandom,它具有更好的加密行为。(请参见,例如,此线程。)


如果希望运行之间的随机序列相同,可以指定种子。通常不希望发生这种情况,所以每次运行和系统都使用不同的种子。currentTimeMillis()是常用的合理种子。如果您面对多个线程,在多个线程同时初始化RNG的情况下,您可能希望避免使用Surviv.TrimeTimeMILISH(),并让Java使用它自己的初始化。


如果您研究Random的默认构造函数的实现,可以看到它在内部使用System.nanoTime()。此外,它使用种子"uniquifier"使随后的种子更加独特。然而,这需要访问static final AtomicLong。因此,如果您有一个高度并发的应用程序,其中许多线程正在构造Random实例,那么最好不要使用默认的构造函数来避免在种子生成上的争用。如果要确保两个线程永远不能获得相同的种子,则应采用默认的构造函数。这也就是说,在实践中,99%的情况下,你采用哪种变体是不相关的。

当TED HOPP正确地声明时,默认构造函数的行为依赖于具体的JDK实现,并且在Java版本之间也有所不同。

另请参阅并发使用java.util.random中的争用,了解与Random类的另一个争用问题。