出于某种原因,我曾经以为java.util.Random是线程不安全的,a-la HashMapBitSet,并且Math.random()的实现是使用Random块或synchronized包装对ThreadLocalRandom.current().nextDouble()的访问。

实际上,结果是 java.util.Random is thread-safe(通过原子)。因此,总而言之:即使我需要在单个线程中进行一些随机输入,使用ThreadLocalRandom还是有道理的,因为里面没有原子的读写操作,编译为锁定指令并发出内存屏障。

而且,由于Java 8上ThreadLocalRandom本质上是一个单例,因此其状态保存在java.lang.Thread类的某些字段中。因此,方法ThreadLocalRandom.current()不能访问ThreadLocalMap,而只能是读取的静态字段。 e。非常便宜。

我有两个问题:

  • 从计算机科学的角度来看,几个线性同余随机生成器的输出(以ThreadLocalRandom的方式初始化)是否与单个线性同余随机生成器(java.util.Random实例)的输出具有相同的“随机性”?
  • 如果第一个问题的答案是"is",那么是否有理由写new Random()(不带种子)而不是ThreadLocalRandom.current()

  • 更新。我以为ThreadLocalRandom.current().ints().parallel().collect(...)之类的调用可能是错误的,因为Thread的随机生成器状态可能未在ForkJoinPool工作线程中初始化,但是ThreadLocalRandom似乎覆盖了ints()longs()doubles()方法,因此上述构造正确。

    最佳答案



    这取决于实现,但是对于Java,它将是,而不是坏,因为Java具有static unique seed atomic long that is manipulated everytime,因此会创建一个Random。但是,使用其他语言或实现我不会感到惊讶,事实并非如此,它们可能只使用系统时间(Java也使用系统时间,但结合使用唯一的种子)。那就是在某些系统上,您可以为多个线程获得相同的种子。

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

    根据维基百科:



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

    当然,这是基于伪随机的。

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



    我无法提出一个特定的用例,但可能是您使用一个ExecutorService来不断创建和处理线程(假设它们没有对此的控制权),但一次也不会很多(例如,最多2个并发线程) 。您可能会发现ThreadLocalRandom更昂贵,而不是创建一个共享的Random。

    给您评论的另一个原因,也许是更好的原因是,您可能想为所有进程重置种子。如果您有一个使用线程的游戏(不是很多,但可以假装),则您可能需要全局重置种子以进行测试,而使用AtomicReference到Random则比尝试将消息传递给所有正在运行的线程要容易得多。

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



    为了解决您对为什么要使用无法重用线程的ExecutorService的其他评论:



    即ThreadPool不一定重用线程。

    09-11 15:59