我想了解使用以System.currentTimeMillis()
作为种子的随机数生成器和仅使用默认构造函数之间的区别。也就是说,这两者之间有什么区别:
Random rand = new Random(System.currentTimeMillis());
还有这个:
Random rand = new Random();
我知道这些数字是伪随机的,但是我还没有完全理解其细节以及它们的生成方式,包括在使用当前时间作为种子和使用默认构造函数时所得到的“随机性”水平之间的差别。我想了解使用以System.currentTimeMillis()
作为种子的随机数生成器和仅使用默认构造函数之间的区别。也就是说,这两者之间有什么区别:
Random rand = new Random(System.currentTimeMillis());
还有这个:
Random rand = new Random();
我知道这些数字是伪随机的,但是我还没有完全理解其细节以及它们的生成方式,包括在使用当前时间作为种子和使用默认构造函数时所得到的“随机性”水平之间的差别。如果你想让随机序列在不同的运行中相同,可以指定一个种子。通常情况下,您不希望这种情况发生,因此每次运行时使用不同的种子是合理的。常用的种子是System.currentTimeMillis()
。
如果您正在编写一个多线程程序,其中多个线程将同时初始化Random
对象,则可能需要避免使用System.currentTimeMillis()
,而是让Java使用其自己的初始化。
System.currentTimeMillis()
有点多余。我是对的吗?事实上,在我的一个程序中,当我使用 Random 类时,我惊讶地发现当我使用 System.currentTimeMillis()
时性能更差。我无法找出原因。我基本上只是感到困惑,但我想,好吧,让我们使用那个有效的方法!顺便说一下,我不知道是否应该将此答案标记为正确,因为真正回答我的问题的是这个评论! - ayePeteSystem.currentTimeMillis
。据我回忆,我在代码的各个点初始化了多个RNG,因此我不知道这是否对生成的值产生了影响。您是否间接建议在整个程序中使用单个RNG?我只使用了一个线程。 - ayePeteSystem.nanoTime()
和一个不断变化的种子来减少多线程冲突。System.currentTimeMillis()
是内置实现的降级版本。故事的寓意是:不要指定种子,除非你想要在多次运行中重复使用种子。 - Andreasprivate static volatile long seedBase = 0;
public Random() {
setSeed(System.nanoTime() + seedBase);
++seedBase;
}
java.security.SecureRandom
,它具有更好的加密行为。(例如,请参见 this thread。)System.nanoTime() + seedBase
在后续调用中相同的概率非常小,在任何情况下,合同只保证“很可能是不同的”。我同意你关于这是一个糟糕的实现的看法;使用AtomicLong
会更好。 - Ted HoppnanoTime()
是非常可能的,特别是考虑到大多数平台实际上并没有提供纳秒级精度(我的 Mac 似乎“只”提供微秒级精度)。但是,像你一样,我认为 AtomicLong 应该是更好的选择 - 至少可以建立良好的习惯! - yshavitRandom
的默认构造函数的实现,您会发现它在内部使用System.nanoTime()
。另外,它使用一个种子"唯一化器"来使后续的种子更加不同。然而,这需要访问一个static final AtomicLong
。因此,如果您有一个高并发的应用程序,在其中许多线程正在构建Random
实例,最好不要使用默认构造函数以避免争用种子生成。如果您想保证两个线程永远不会得到相同的种子,那么应该使用默认构造函数。Random
类的另一个争用问题。AtomicLong
,可以避免您所描述的多线程问题。 - Ted HoppAtomicLong
是否是一个问题,我的观点只是,如果像你所说,“你查看Random
默认构造函数的实现”,则会发现根据你使用的Java版本不同而有不同的事情。在某些实现中,“唯一标识符”只是一个volatile
字段。虽然没有线程竞争,但存在竞争条件(也没有确保唯一性)。 - Ted Hopp