随机类线程安全吗?

Posted

技术标签:

【中文标题】随机类线程安全吗?【英文标题】:Is Random class thread safe? 【发布时间】:2011-08-14 17:56:02 【问题描述】:

在多个线程之间共享Random 类的一个实例是否有效?特别是从多个线程调用nextInt(int)

【问题讨论】:

小心使用 Random 在多线程环境中获取数字可能会给您带来不好的结果。也许没关系,但如果您正在做一些模拟,很高兴知道。 对于更多读者:有一个 1.7 的新类,名为 java.util.concurrent.ThreadLocalRandom 提防Contention in concurrent use of java.util.Random。 【参考方案1】:

根据 Java 6 文档,Math.random() 保证它可以安全地被多个线程使用。但是 Random 类没有。我假设你必须自己同步。

编辑:

不过,根据接受的答案,自 Java 7 以来文档似乎发生了变化,而 Random 类似乎也提供了这种保证。

【讨论】:

在 java 1.8 (RedHat OpenJDK) 下,Math.random() 实现如下所示:java public static double random() return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble(); // ... private static final class RandomNumberGeneratorHolder static final Random randomNumberGenerator = new Random(); 我不知道它在 java.util.Random 时如何被认为是线程安全的不是…… « 根据文档 » - 不是 « 根据我在实施中看到的 »。实施细节可能会有所不同,我只是坚持文档所说的内容。不过,根据接受的答案,自 Java 7 以来,文档似乎发生了变化,而 Random 类似乎也提供了这种保证。【参考方案2】:

如上所述,它是线程保存,但根据this article 使用java.util.concurrent.ThreadLocalRandom 可能是明智之举(链接失效)。 ThreadLocalRandom 也是 Random 的子类,因此向后兼容。

文章链接比较了不同 Random 的分析结果 类:java.util.Randomjava.util.concurrent.ThreadLocalRandomjava.lang.ThreadLocal<java.util.Random>。结果表明, ThreadLocalRandom 的使用性能最高,其次是 ThreadLocal 和性能最差的 Random 本身。

【讨论】:

【参考方案3】:

这是我在不假设 Random 使用原子变量的情况下处理问题的方法。如果currentTime * thread id 在未来某个时间相等,它仍然可以随机碰撞,但这对于我的需要来说已经很少见了。为了真正避免发生冲突的可能性,您可以让每个请求等待一个唯一的时钟时间戳

/**
 * Thread-specific random number generators. Each is seeded with the thread
 * ID, so the sequence of pseudo-random numbers are unique between threads.
 */
private static ThreadLocal<Random> random = new ThreadLocal<Random>() 
    @Override
    protected Random initialValue() 
        return new Random(
            System.currentTimeMillis() *
            Thread.currentThread().getId());
    
;

【讨论】:

起来!问:(24*60*60*1000) 部分重要吗? 是的,这是一个肮脏的修复。 (24*60*60*1000) 使得xxxxxxxxxx045millis 的 ID 为 12 的线程不会像 xxxxxxxxxx035millis 的线程 22 一样被播种。但是,我没有任何充分的理由假设线程 ID 是递增的,也没有充分的理由认为我明天会在比今天更随机的时间创建线程。我现在简化了算法并更新了描述以找出缺点。【参考方案4】:

没有理由多个线程不能都使用同一个 Random。但是,由于该类不是明确的线程安全的,并且通过种子维护一系列伪随机数。多个线程可能以相同的随机数结束。最好为每个线程创建多个随机数并以不同方式播种。

编辑:我刚刚注意到 Sun 实现使用 AtomicLong,所以我猜这是线程安全的(正如 Peter Lawrey (+1) 所指出的)。

EDIT2:OpenJDK 还使用 AtomicLong 作为种子。正如其他人所说,尽管依靠这个仍然不好。

【讨论】:

【参考方案5】:

它是线程安全的,因为它在被多个线程使用时仍然会生成随机数。

Sun/Oracle JVM 实现使用 synchronized 和 AtomicLong 作为种子来提高线程间的一致性。但文档中的所有平台似乎都不能保证它。

我不会编写你的程序来要求这样的保证,尤其是因为你无法确定调用nextInt() 的顺序。

【讨论】:

Java 7 文档中添加了保证:“java.util.Random 的实例是线程安全的。” docs.oracle.com/javase/7/docs/api/java/util/Random.html【参考方案6】:

是的,Random 是线程安全的。 nextInt() 方法调用受保护的next(int) 方法,该方法使用AtomicLong seed, nextseed(原子长)来生成下一个种子。 AtomicLong 用于种子生成时的线程安全。

【讨论】:

【参考方案7】:

Random 类未设置为在多个线程中使用一个实例。当然,如果您这样做了,您可能会增加获得不可预测且更接近随机数字的可能性。但由于它是一个伪随机生成器,我看不出你为什么需要共享一个实例。有更具体的要求吗?

【讨论】:

【参考方案8】:

它是线程安全的,尽管并非总是如此。

更多详情请见http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6362070。

【讨论】:

以上是关于随机类线程安全吗?的主要内容,如果未能解决你的问题,请参考以下文章

SecureRandom 线程安全吗?

Jetty websocket 客户端类 WebSocketClient 线程安全吗?

java priorityblockingqueue 线程安全吗

Java套接字多线程安全吗?

这是线程安全队列类的正确方法吗?

c++ string线程安全吗