ThreadLocalRandom
Guide to using ThreadLocalRandom with examples.
We'll cover the following
If you are interviewing, consider buying our number#1 course for Java Multithreading Interviews.
Overview
The class java.util.concurrent.ThreadLocalRandom
is derived from java.util.Random
and generates random numbers much more efficiently than java.util.Random
in multithreaded scenarios. Interestingly, Random
is thread-safe and can be used by multiple threads without malfunction, just not efficiently.
To understand why an instance of the Random
class experiences overhead and contention in concurrent programs, we’ll delve into the code for one of the most commonly used methods nextInt()
of the Random
class. The code is reproduced verbatim from the Java source code below:
/**
* Generates the next pseudorandom number. Subclasses should
* override this, as this is used by all other methods.
*
* <p>The general contract of {@code next} is that it returns an
* {@code int} value and if the argument {@code bits} is between
* {@code 1} and {@code 32} (inclusive), then that many low-order
* bits of the returned value will be (approximately) independently
* chosen bit values, each of which is (approximately) equally
* likely to be {@code 0} or {@code 1}. The method {@code next} is
* implemented by class {@code Random} by atomically updating the seed to
* <pre>{@code (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)}</pre>
* and returning
* <pre>{@code (int)(seed >>> (48 - bits))}.</pre>
*
* This is a linear congruential pseudorandom number generator, as
* defined by D. H. Lehmer and described by Donald E. Knuth in
* <i>The Art of Computer Programming,</i> Volume 2:
* <i>Seminumerical Algorithms</i>, section 3.2.1.
*
* @param bits random bits
* @return the next pseudorandom value from this random number
* generator's sequence
* @since 1.1
*/
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
Examine the code above and realize the do-while
loop uses the compareAndSet()
method to atomically set the seed
variable to a new value in its predicate. Imagine several threads invoking the next()
method on a shared instance of Random
, only one thread will successfully exit the loop and the rest will re-execute the loop, until all of them exit one by one. This mechanism to update the seed
variable is precisely what makes the Random
class inefficient for highly concurrent programs, when several threads want to generate random numbers in parallel.
The performance issues faced by Random
are addressed by the ThreadLocalRandom
class which is isolated in its effects to a single thread. A random number generated by one thread using ThreadLocalRandom
has no bearing on random numbers generated by other threads, unlike an instance of Random
that generates random numbers globally. Furthermore, ThreadLocalRandom
differs from Random
in that the former doesn’t allow setting a seed value unlike the latter. In summary, ThreadLocalRandom
is more performant than Random
as it eliminates concurrent access to shared state.
The astute reader would question if maintaining a distinct Random
object per thread is equivalent to using the ThreadLocalRandom
class? The ThreadLocalRandom
class is singleton and uses state held by the Thread
class to generate random numbers. In particular the Thread
class houses the following fields for ThreadLocalRandom
to use for generating random numbers and related book-keeping.
class Thread implements Runnable {
// The following three initially uninitialized fields are exclusively
// managed by class java.util.concurrent.ThreadLocalRandom. These
// fields are used to build the high-performance PRNGs in the
// concurrent code, and we can not risk accidental false sharing.
// Hence, the fields are isolated with @Contended.
/** The current seed for a ThreadLocalRandom */
@jdk.internal.vm.annotation.Contended("tlr")
long threadLocalRandomSeed;
/** Probe hash value; nonzero if threadLocalRandomSeed initialized */
@jdk.internal.vm.annotation.Contended("tlr")
int threadLocalRandomProbe;
/** Secondary seed isolated from public ThreadLocalRandom sequence */
@jdk.internal.vm.annotation.Contended("tlr")
int threadLocalRandomSecondarySeed;
}
Each thread stores the seed itself in the field threadLocalRandomSeed
. As the seed is not shared among threads anymore, performance improves.
Usage
The idiomatic usage for generating random numbers takes the form of ThreadLocalRandom.current().nextInt()
and is demonstrated in the widget below:
Create a free account to view this lesson.
By signing up, you agree to Educative's Terms of Service and Privacy Policy