Java 8,有没有理由写new Random()

Is there any reason to write `new Random()` since Java 8?

出于某种原因,我曾经认为java.util.Random是线程不安全的,a-la HashMapBitSetMath.random()被实现为用synchronized块包装访问RandomThreadLocalRandom.current().nextDouble()块。

事实证明,java.util.Random是线程安全的(通过原子)。因此带来的好处是:即使我在一个线程中需要一些随机输入,使用ThreadLocalRandom也是有意义的,因为里面没有原子读写,编译成锁定的指令,发出内存屏障。

此外,由于Java 8,EDCOX1,8,它本质上是一个单体,它的状态被保存在EDOCX1×10×类的一些字段中。因此,方法ThreadLocalRandom.current()不是对ThreadLocalMap的访问,而是一个静态字段读取,即非常便宜。

我有两个问题:

  • 从计算机科学的角度来看,几个线性同余随机发生器的输出(初始化方式为ThreadLocalRandoms)是否与单个线性同余随机发生器的输出(java.util.Random实例)相同?

  • 如果第一个问题的答案是"是",那么是否有理由写结构EDOCX1(无种子)而不是ThreadLocalRandom.current()

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


    1...

    这取决于实现,但是对于Java来说,它将是"罢工">相同的< /罢工>并不是那么糟糕,因为Java有一个静态的唯一的种子原子长,每次创建随机时都会被操作。然而,在其他语言或实现中,我不会感到惊讶。事实并非如此,它们可能只使用系统时间(Java也使用系统时间,但组合使用唯一的种子)。在某些系统上,您可以为多个线程获得相同的种子。

    经过进一步的检查和一些实际的测试(尽管是脆弱的测试),我以前可能是错的,因为在同一时间使用许多(我说的是100K)随机数生成器实际上更糟(即使它们是不同的实例)。我不完全确定它的种子碰撞或者仅仅是实际的全局种子增量是否可以预测。当然,这可能只是我的测试工具或方法。

    根据维基百科:

    Random number generators, particularly for parallel computers, should not be trusted.[12] It is strongly recommended to check the results of simulation with more than one RNG to check if bias is introduced. Among the recommended generators for use on a parallel computer include combined linear congruential generators using sequence splitting and lagged Fibonacci generators using independent sequences.

    所以从理论上讲,它应该更好,因为threadlocalrandom会创建独立的序列,所以我的测试可能有缺陷。

    这当然是基于伪随机的。

    物理随机性或基于实际熵的安全随机生成器可能会导致差异(即或多或少的熵),但我不是专家,我没有访问权限。

    2...

    我不能想出一个特定的用例,但有一个可能是您使用的ExecutorService会不断地创建和处理线程(假定它们没有控制权),但不会同时处理多个线程(即最多2个并发线程)。您可能会发现threadLocalRandom更昂贵,而不是创建单个共享随机对象。

    考虑到您的评论,另一个可能更好的原因是您可能希望为所有进程重置种子。如果你有一个游戏使用线程(不多,但让我们假设一下),你可能想全局重置种子进行测试,这比使用随机原子引用来传递消息给所有正在运行的线程要容易得多。

    您可能不想使用threadLocalRandom的另一个原因是平台原因。一些平台对线程创建有特定的要求,因此线程本地创建。因此,要解决"你比Randoms有更大的问题",请查看Google应用程序,其中:

    A Java application can create a new thread, but there are some restrictions on how to do it. These threads can't"outlive" the request that creates them. (On a backend server, an application can spawn a background thread, a thread that can"outlive" the request that creates it.)

    为了解决您对为什么使用无法重用线程的ExecutorService的附加意见:

    or use the factory object returned by com.google.appengine.api.ThreadManager.currentRequestThreadFactory() with an ExecutorService (e.g., call Executors.newCachedThreadPool(factory)).

    也就是说,线程池不一定要重用线程。