出于某种原因,我曾经以为java.util.Random
是线程不安全的,a-la HashMap
或BitSet
,并且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
实例)的输出具有相同的“随机性”? 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不一定重用线程。