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.concurrent provides ThreadLocalRandom, a convenient random number generator for use in multiple threads or ForkJoinTasks. Replacing Math.random() with ThreadLocalRandom reduces contention and improves performance. Use ThreadLocalRandom.current() and call its methods to obtain random numbers. Example:
int randomNum = ThreadLocalRandom.current().nextInt(max);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 Random class 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:
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;
}The core computation is:
nextseed = (oldseed * multiplier + addend) & mask;
Which expands to:
nextseed = (oldseed * 25214903917L + 11L) & 281474976710655L;
Here multiplier and addend correspond to a and c; mask implements modulo 2 48 .
ThreadLocalRandom Source Code Analysis
Obtain an instance via ThreadLocalRandom.current(). If not initialized, localInit() is called.
public static ThreadLocalRandom current() {
if (UNSAFE.getInt(Thread.currentThread(), PROBE) == 0)
localInit();
return instance;
} localInit()initializes per‑thread seed and probe:
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);
}Random numbers are generated without CAS on a shared object, using the thread’s own seed. nextInt(int bound) works similarly to Random but uses mix32(nextSeed()):
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;
} mix32mixes the seed to produce a 32‑bit value:
private static int mix32(long z) {
z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
return (int)(((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32);
} nextSeed()updates the per‑thread seed:
final long nextSeed() {
Thread t; long r;
UNSAFE.putLong(t = Thread.currentThread(), SEED,
r = UNSAFE.getLong(t, SEED) + GAMMA);
return r;
}Usage Examples
Random
Example of using java.util.Random in multiple threads (shared instance is thread‑safe but may suffer contention):
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();
}
}
}ThreadLocalRandom
Simple example using ThreadLocalRandom:
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();
}
}
}Sample output:
randomNum: 648582
randomNum: 76984
randomNum: 561085
randomNum: 120131
randomNum: 210919
randomNum: 546645
randomNum: 194225
randomNum: 930034
randomNum: 203882Summary
Avoid sharing a Random instance across threads; although Random is thread‑safe, contention on the seed degrades performance. Since JDK7, use ThreadLocalRandom directly; for earlier JDK versions, ensure each thread holds its own Random instance.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
