Boost Java Concurrency Performance with ThreadLocalRandom: Deep Dive & Code
This article explains how ThreadLocalRandom in JDK7 improves multithreaded random number generation, compares it with Random and Math.random(), analyzes the underlying linear congruential algorithm, and provides practical code examples for high‑performance Java applications.
Introduction
In JDK7,
java.util.concurrentprovides
ThreadLocalRandom, a convenient random number generator for use in multiple threads or
ForkJoinTasks. Replacing
Math.random()with
ThreadLocalRandomreduces contention and improves performance. Use
ThreadLocalRandom.current()and call its methods to obtain random numbers. Example:
<code>int randomNum = ThreadLocalRandom.current().nextInt(max);</code>Source Code Analysis
Linear Congruential Method
The linear congruential method (LCG) is a classic algorithm for generating uniformly distributed numbers in [0,1]. It includes mixed and multiplicative variants, proposed by Lehmer in 1951. Java's
Randomclass implements this algorithm.
X[n + 1] = (a * X[n] + c) mod m
Parameters:
m > 0 modulus
0 ≤ a < m multiplier
0 ≤ c < m increment
0 ≤ X0 < m seed
Random Source Code Analysis
Key fields in
Random:
<code>private static final long multiplier = 0x5DEECE66DL;
private static final long addend = 0xBL;
private static final long mask = (1L << 48) - 1;
private 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));
}
public int nextInt(int bound) {
if (bound <= 0) throw new IllegalArgumentException(BadBound);
int r = next(31);
int m = bound - 1;
if ((bound & m) == 0) // power of two
r = (int)((bound * (long)r) >>> 31);
else {
for (int u = r; u - (r = u % bound) + m < 0; u = next(31));
}
return r;
}
</code>The core computation is:
nextseed = (oldseed * multiplier + addend) & mask;
Which expands to:
nextseed = (oldseed * 25214903917L + 11L) & 281474976710655L;
Here
multiplierand
addendcorrespond to
aand
c;
maskimplements modulo 2 48 .
ThreadLocalRandom Source Code Analysis
Obtain an instance via
ThreadLocalRandom.current(). If not initialized,
localInit()is called.
<code>public static ThreadLocalRandom current() {
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
}
</code> localInit()initializes per‑thread seed and probe:
<code>static final void localInit() {
int p = probeGenerator.addAndGet(PROBE_INCREMENT);
int probe = (p == 0) ? 1 : p; // skip 0
long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
Thread t = Thread.currentThread();
UNSAFE.putLong(t, SEED, seed);
UNSAFE.putInt(t, PROBE, probe);
}
</code>Random numbers are generated without CAS on a shared object, using the thread’s own seed.
nextInt(int bound)works similarly to
Randombut uses
mix32(nextSeed()):
<code>public int nextInt(int bound) {
if (bound <= 0) throw new IllegalArgumentException(BadBound);
int r = mix32(nextSeed());
int m = bound - 1;
if ((bound & m) == 0) // power of two
r &= m;
else {
for (int u = r >>> 1; u + m - (r = u % bound) < 0; u = mix32(nextSeed()) >>> 1);
}
return r;
}
</code> mix32mixes the seed to produce a 32‑bit value:
<code>private static int mix32(long z) {
z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32);
}
</code> nextSeed()updates the per‑thread seed:
<code>final long nextSeed() {
Thread t; long r;
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}
</code>Usage Examples
Random
Example of using
java.util.Randomin multiple threads (shared instance is thread‑safe but may suffer contention):
<code>public class RandomTest {
static Random RANDOM = new Random();
public static void main(String[] args) {
final int max = 1000000;
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
int randomNum = RANDOM.nextInt(max);
System.out.println("randomNum: " + randomNum);
}).start();
}
}
}
</code>ThreadLocalRandom
Simple example using
ThreadLocalRandom:
<code>public class ThreadLocalRandomTest {
public static void main(String[] args) {
final int max = 1000000;
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
int randomNum = ThreadLocalRandom.current().nextInt(max);
System.out.println("randomNum: " + randomNum);
}).start();
}
}
}
</code>Sample output:
<code>randomNum: 648582
randomNum: 76984
randomNum: 561085
randomNum: 120131
randomNum: 210919
randomNum: 546645
randomNum: 194225
randomNum: 930034
randomNum: 203882
</code>Summary
Avoid sharing a Random instance across threads; although Random is thread‑safe, contention on the seed degrades performance. Since JDK7, use
ThreadLocalRandomdirectly; for earlier JDK versions, ensure each thread holds its own Random instance.
Ops Development Stories
Maintained by a like‑minded team, covering both operations and development. Topics span Linux ops, DevOps toolchain, Kubernetes containerization, monitoring, log collection, network security, and Python or Go development. Team members: Qiao Ke, wanger, Dong Ge, Su Xin, Hua Zai, Zheng Ge, Teacher Xia.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.