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()
.
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.
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.
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 (the java.util.Random
instance)?
If answer to the first question is Yes, is there any reason to write the construction new Random()
(without seed) instead of ThreadLocalRandom.current()
, ever?
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.
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.
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.
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.
So in theory it is supposed to be better since ThreadLocalRandom would create independent sequences so maybe my testing is flawed.
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.
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.
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:
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.)
And to address your additional comment of why would you use an ExecutorService that can't reuse threads:
or use the factory object returned by com.google.appengine.api.ThreadManager.currentRequestThreadFactory() with an ExecutorService (e.g., call Executors.newCachedThreadPool(factory)).
ie a ThreadPool that doesn't necessarily reuse threads.