随机类线程安全吗?
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.Random
,java.util.concurrent.ThreadLocalRandom
和java.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)
使得xxxxxxxxxx045
millis 的 ID 为 12
的线程不会像 xxxxxxxxxx035
millis 的线程 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。
【讨论】:
以上是关于随机类线程安全吗?的主要内容,如果未能解决你的问题,请参考以下文章
Jetty websocket 客户端类 WebSocketClient 线程安全吗?