2015-04-17 58 views
26

出于某种原因,我曾经认为java.util.Random是线程不安全的,一拉HashMapBitSet,和Math.random()被实现为包访问Random与​​块,或ThreadLocalRandom.current().nextDouble()是否有任何理由从Java 8开始编写`new Random()`?

事实上,事实证明,java.util.Random is thread-safe(通过原子)。因此,即使我需要在单个线程中进行一些随机输入,使用ThreadLocalRandom也是有意义的,因为内部没有原子读取和写入,编译为锁定指令并发出内存屏障。而且,由于Java 8,ThreadLocalRandom实质上是一个单例,所以它的状态保存在java.lang.Thread类的一些字段中。因此方法ThreadLocalRandom.current()不是对ThreadLocalMap的访问,而只是一个静态字段读取,即。即非常便宜。

我有两个问题:

  1. 但从计算机科学一点,就是几个线性同余随机发生器(初始化的方法ThreadLocalRandom s为)的输出是相同的“随机”为输出单线性同余随机发生器(java.util.Random实例)?

  2. 如果回答第一个问题是肯定的,是有任何理由写的建设new Random()(不含种子),而不是ThreadLocalRandom.current()永远不会消失?

更新。我认为像ThreadLocalRandom.current().ints().parallel().collect(...)这样的调用可能不正确,因为线程的随机生成器状态可能不会在ForkJoinPool工作线程中初始化,但似乎ThreadLocalRandom覆盖方法ints(),longs()doubles(),使上述构造正确。

+3

你不能用ThreadLocalRandom写出http://stackoverflow.com/questions/15182496/why-does-this-code-using-random-strings-print-hello-world。 – immibis

+0

@immibis好吧,它意味着唯一的情况 - 当我需要重现一些随机数序列时 – leventov

+0

@immibis然而,问题是关于具体构造'new Random()',i。即没有种子。问题是开放的。 – leventov

回答

3

1 ...

这取决于实现,但对Java这将是 相同 不是那么糟糕,因为 Java有一个static unique seed atomic long that is manipulated everytime随机创建。但是,在其他语言或实现中,我不会感到惊讶,并且他们可能只是使用系统时间(Java使用系统时间,但使用独特的种子组合)。那是在一些系统上,你可以为多个线程获得相同的种子。

进一步的检查和一些实际的测试(虽然脆测试)后,它会出现,我可能在这之前已经错了,它实际上是雪上加霜使用许多在同一时间(我说的是100K)随机数发生器(即使它们是不同的实例)。我不完全确定它的种子碰撞还是实际的全球种子增量变得可预测的事实。当然,这可能只是我的测试工具或方法。

根据维基百科:

随机数生成器,特别是并行计算机,不应该被信任。[12]强烈建议用多于一个RNG检查模拟结果,以检查是否引入偏差。推荐用于并行计算机的发生器包括联合线性同余发生器,使用序列分裂和滞后斐波那契发生器,使用独立序列。

所以理论上它应该是更好的,因为ThreadLocalRandom会创建独立的序列,所以也许我的测试是有缺陷的。

这当然是基于伪随机的。

物理随机性或基于实际熵的安全随机生成器可能会导致差异(即更多/更少的熵),但我不是专家,我无法访问一个。

2 ...

我不能拿出一个特定用例,但一个可能是您使用的不断创造和线程(处置的假设他们没有一个ExecutorService控制这一点),但从来没有多少(即最多2个并发线程)。您可能会发现ThreadLocalRandom更加昂贵,而不是创建单个共享的Random。

给出您的意见的另一个原因和可能更好的理由是,你可能想重置所有进程的种子。如果你有一个使用线程的游戏(不是很多,但是让我们假装),你可能想要为了测试目的而重新设置种子,这比使用AtomicReference来更容易,而不是尝试将消息传递给所有正在运行的线程。

您可能不想使用ThreadLocalRandom的另一个原因是平台的原因。一些平台对线程创建和线程本地创建有特殊要求。因此,解决“你有一个更大的问题,比随机量”退房Google Apps其中:

Java应用程序可以创建一个新的线程,但对如何做到这一点一些限制。这些线程不能“超越”创建它们的请求。 (在后端服务器,应用程序可以生成一个后台线程,线程,可以“活得长”创建它的请求。)

,并解决您的额外的你为什么要使用一个ExecutorService,可”评论吨重用线程:

,或者使用()与由ExecutorService的返回com.google.appengine.api.ThreadManager.currentRequestThreadFactory工厂对象(例如,呼叫Executors.newCachedThreadPool(工厂))。

即不需要重用线程的ThreadPool。

+0

1。这个问题具体是关于Java的。如果它是完全随机的,为什么'Math.random()'不是'ThreadLocalRandom.current()。nextDouble()'? – leventov

+2

2.线程的创建和处理比在此线程中初始化随机生成器要昂贵得多。所以,如果你的ExecutorService不断地创建和删除线程(通过ExecutorServices的主要目的 - 重新使用线程),你会遇到比randoms更大的问题... – leventov

+2

@leventov当然我同意你的观点。我不认为你明白ThreadLocalRandom比Random更年轻,并不是所有的运行时都像线程局部变量一样。 ThreadLocal变量被放到Java版本1.2中,其中Random是版本1.0。如果你在不同的平台上,你也可能无法控制ExecutorService ...有一些平台不能控制线程创建,并且Threadlocal变量可能在每次调度时被删除。并非所有的东西都是Java SE企业! –

相关问题