For some reason I used to think that java.util.Random
is thread-unsafe, a-la HashMap
or BitSet
, and Math.random()
is implemented either as wrapping access to Random
with synchronized
block, or ThreadLocalRandom.current().nextDouble()
.
出于某种原因,我曾经认为java.util.Random是线程不安全的,a-la HashMap或BitSet,而Math.random()实现为包含对带有synchronized块的Random或ThreadLocalRandom.current()的访问。 nextDouble()。
Actually it turns out that java.util.Random
is thread-safe (via atomics). Hence the takeaway: even if I need some random input in a single thread, it makes sense to use ThreadLocalRandom
, because there isn't atomic reads and writes inside, compiled as locked instructions and emitting memory barriers.
实际上,事实证明java.util.Random是线程安全的(通过atomics)。因此需要注意:即使我需要在单个线程中进行一些随机输入,使用ThreadLocalRandom也是有意义的,因为内部没有原子读写,编译为锁定指令并释放内存障碍。
Moreover, since Java 8, ThreadLocalRandom
is essentially a singleton, its state is kept in some fields of java.lang.Thread
class. Therefore method ThreadLocalRandom.current()
is not an access to ThreadLocalMap
, but just a static field read, i. e. very cheap.
而且,由于Java 8,ThreadLocalRandom本质上是一个单例,它的状态保存在java.lang.Thread类的某些字段中。因此,方法ThreadLocalRandom.current()不是对ThreadLocalMap的访问,而只是一个静态字段读取,i。即非常便宜。
I have two questions:
我有两个问题:
-
From Computer Science point of view, is the output of several linear congruential random generators (initialized the way
ThreadLocalRandom
s are) is same "random" as an output of the single linear congruential random generator (thejava.util.Random
instance)?从计算机科学的角度来看,几个线性同余随机生成器(以ThreadLocalRandoms的方式初始化)的输出是否与单个线性同余随机生成器(java.util.Random实例)的输出相同“随机”?
-
If answer to the first question is Yes, is there any reason to write the construction
new Random()
(without seed) instead ofThreadLocalRandom.current()
, ever?如果对第一个问题的答案是肯定的,是否有理由编写构造新的Random()(没有种子)而不是ThreadLocalRandom.current()?
Update. I supposed that calls like ThreadLocalRandom.current().ints().parallel().collect(...)
could be incorrect, because Thread's random generator state might be not initialized in ForkJoinPool
worker threads, but appears that ThreadLocalRandom
overrides methods ints()
, longs()
and doubles()
, making the above construction correct.
更新。我认为调用类似ThreadLocalRandom.current()。ints()。parallel()。collect(...)可能不正确,因为Thread的随机生成器状态可能未在ForkJoinPool工作线程中初始化,但看起来ThreadLocalRandom覆盖方法整数(),longs()和double(),使上述结构正确。
1 个解决方案
#1
2
1...
It depends on implementation but for Java it will be the
same
not as bad because Java has a static unique seed atomic long that is manipulated everytime a Random is created. However I would not be surprised in other languages or implementations this is not the case and they might just use the system time (Java uses the system time as well but uses the unique seed in combination). That is on some systems you could get the same seed for multiple threads.
这取决于实现,但对于Java来说,它将是相同的而不是坏的,因为Java具有静态唯一种子原子长度,每次创建Random时都会对其进行操作。但是我不会对其他语言或实现情况感到惊讶,事实并非如此,他们可能只使用系统时间(Java也使用系统时间,但组合使用唯一种子)。在某些系统上,您可以获得多个线程的相同种子。
After further examination and some actual testing (albeit brittle testing) it appears I might have been wrong before in that it is actually worse to use many (I'm talking 100k) Random number generators at the same time (even though they are different instances). I'm not entirely sure if its seed collision or just the fact that the actual global seed incrementing becomes predictable. Of course this could just be my testing harness or methodology.
经过进一步的检查和一些实际的测试(虽然是脆弱的测试),看起来我可能错了之前,实际上更糟糕的是使用很多(我说的是100k)同时的随机数生成器(即使它们是不同的实例) )。我不完全确定它的种子冲突还是实际全球种子增量变得可预测的事实。当然,这可能只是我的测试工具或方法。
According to wikipedia:
根据*:
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.
随机数生成器,特别是并行计算机,不应该被信任。[12]强烈建议使用多个RNG检查模拟结果,以检查是否引入了偏差。在并行计算机上使用的推荐发生器包括使用序列分裂的组合线性同余生成器和使用独立序列的滞后Fibonacci生成器。
So in theory it is supposed to be better since ThreadLocalRandom would create independent sequences so maybe my testing is flawed.
因此理论上它应该更好,因为ThreadLocalRandom会创建独立的序列,所以我的测试可能存在缺陷。
This is of course based on pseudo random.
这当然基于伪随机。
Physical randomness or a secure random generator based on actually entropy might result in differences (ie more/less entropy) but I'm not an expert and I don't have access to one.
物理随机性或基于实际熵的安全随机生成器可能导致差异(即更多/更少的熵),但我不是专家,我无法访问一个。
2...
I can't come up with a particular use case but one might be that you use an ExecutorService that constantly creates and disposes of threads (assume they don't have control of this) but never many at once (ie max 2 concurrent threads). You might find the ThreadLocalRandom to be more expensive instead of creating a single shared Random.
我不能提出一个特定的用例,但可能是你使用了一个不断创建和处理线程的ExecutorService(假设它们没有对此的控制)但是从来没有多次(即最多2个并发线程) 。您可能会发现ThreadLocalRandom更昂贵,而不是创建单个共享Random。
Another reason and probably better reason given your comments is that you might want to reset the seed for all processes. If you have a game that uses threads (not many do but lets pretend) you might want to global reset the seed for testing purposes which is far easier with an AtomicReference to a Random than trying to message pass to all the running threads.
给出评论的另一个原因和可能更好的理由是您可能想要为所有进程重置种子。如果你有一个使用线程的游戏(不是很多但是让我们假装)你可能想要全局重置种子用于测试目的,使用AtomicReference到Random要比尝试将消息传递给所有正在运行的线程容易得多。
Another reason you might not want to use ThreadLocalRandom are platform reasons. Some platforms have specific requirements on thread creation and thus threadlocal creation. Thus to address "you have a bigger problem, than randoms" check out Google Apps where:
您可能不想使用ThreadLocalRandom的另一个原因是平台原因。某些平台对线程创建有特定要求,因此对线程局部创建有特定要求。因此,要解决“你有一个更大的问题,而不是randoms”,请查看Google Apps:
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.)
Java应用程序可以创建新线程,但是对于如何执行它有一些限制。这些线程不能“创建”创建它们的请求“寿命”。 (在后端服务器上,应用程序可以生成后台线程,这个线程可以“创建”创建它的请求“寿命”。)
And to address your additional comment of why would you use an ExecutorService that can't reuse threads:
并解决您为什么要使用无法重用线程的ExecutorService的额外注释:
or use the factory object returned by com.google.appengine.api.ThreadManager.currentRequestThreadFactory() with an ExecutorService (e.g., call Executors.newCachedThreadPool(factory)).
或者使用com.google.appengine.api.ThreadManager.currentRequestThreadFactory()返回的工厂对象和ExecutorService(例如,调用Executors.newCachedThreadPool(factory))。
ie a ThreadPool that doesn't necessarily reuse threads.
即一个不一定重用线程的ThreadPool。
#1
2
1...
It depends on implementation but for Java it will be the
same
not as bad because Java has a static unique seed atomic long that is manipulated everytime a Random is created. However I would not be surprised in other languages or implementations this is not the case and they might just use the system time (Java uses the system time as well but uses the unique seed in combination). That is on some systems you could get the same seed for multiple threads.
这取决于实现,但对于Java来说,它将是相同的而不是坏的,因为Java具有静态唯一种子原子长度,每次创建Random时都会对其进行操作。但是我不会对其他语言或实现情况感到惊讶,事实并非如此,他们可能只使用系统时间(Java也使用系统时间,但组合使用唯一种子)。在某些系统上,您可以获得多个线程的相同种子。
After further examination and some actual testing (albeit brittle testing) it appears I might have been wrong before in that it is actually worse to use many (I'm talking 100k) Random number generators at the same time (even though they are different instances). I'm not entirely sure if its seed collision or just the fact that the actual global seed incrementing becomes predictable. Of course this could just be my testing harness or methodology.
经过进一步的检查和一些实际的测试(虽然是脆弱的测试),看起来我可能错了之前,实际上更糟糕的是使用很多(我说的是100k)同时的随机数生成器(即使它们是不同的实例) )。我不完全确定它的种子冲突还是实际全球种子增量变得可预测的事实。当然,这可能只是我的测试工具或方法。
According to wikipedia:
根据*:
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.
随机数生成器,特别是并行计算机,不应该被信任。[12]强烈建议使用多个RNG检查模拟结果,以检查是否引入了偏差。在并行计算机上使用的推荐发生器包括使用序列分裂的组合线性同余生成器和使用独立序列的滞后Fibonacci生成器。
So in theory it is supposed to be better since ThreadLocalRandom would create independent sequences so maybe my testing is flawed.
因此理论上它应该更好,因为ThreadLocalRandom会创建独立的序列,所以我的测试可能存在缺陷。
This is of course based on pseudo random.
这当然基于伪随机。
Physical randomness or a secure random generator based on actually entropy might result in differences (ie more/less entropy) but I'm not an expert and I don't have access to one.
物理随机性或基于实际熵的安全随机生成器可能导致差异(即更多/更少的熵),但我不是专家,我无法访问一个。
2...
I can't come up with a particular use case but one might be that you use an ExecutorService that constantly creates and disposes of threads (assume they don't have control of this) but never many at once (ie max 2 concurrent threads). You might find the ThreadLocalRandom to be more expensive instead of creating a single shared Random.
我不能提出一个特定的用例,但可能是你使用了一个不断创建和处理线程的ExecutorService(假设它们没有对此的控制)但是从来没有多次(即最多2个并发线程) 。您可能会发现ThreadLocalRandom更昂贵,而不是创建单个共享Random。
Another reason and probably better reason given your comments is that you might want to reset the seed for all processes. If you have a game that uses threads (not many do but lets pretend) you might want to global reset the seed for testing purposes which is far easier with an AtomicReference to a Random than trying to message pass to all the running threads.
给出评论的另一个原因和可能更好的理由是您可能想要为所有进程重置种子。如果你有一个使用线程的游戏(不是很多但是让我们假装)你可能想要全局重置种子用于测试目的,使用AtomicReference到Random要比尝试将消息传递给所有正在运行的线程容易得多。
Another reason you might not want to use ThreadLocalRandom are platform reasons. Some platforms have specific requirements on thread creation and thus threadlocal creation. Thus to address "you have a bigger problem, than randoms" check out Google Apps where:
您可能不想使用ThreadLocalRandom的另一个原因是平台原因。某些平台对线程创建有特定要求,因此对线程局部创建有特定要求。因此,要解决“你有一个更大的问题,而不是randoms”,请查看Google Apps:
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.)
Java应用程序可以创建新线程,但是对于如何执行它有一些限制。这些线程不能“创建”创建它们的请求“寿命”。 (在后端服务器上,应用程序可以生成后台线程,这个线程可以“创建”创建它的请求“寿命”。)
And to address your additional comment of why would you use an ExecutorService that can't reuse threads:
并解决您为什么要使用无法重用线程的ExecutorService的额外注释:
or use the factory object returned by com.google.appengine.api.ThreadManager.currentRequestThreadFactory() with an ExecutorService (e.g., call Executors.newCachedThreadPool(factory)).
或者使用com.google.appengine.api.ThreadManager.currentRequestThreadFactory()返回的工厂对象和ExecutorService(例如,调用Executors.newCachedThreadPool(factory))。
ie a ThreadPool that doesn't necessarily reuse threads.
即一个不一定重用线程的ThreadPool。