Backend Development 9 min read

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.

Ops Development Stories
Ops Development Stories
Ops Development Stories
Boost Java Concurrency Performance with ThreadLocalRandom: Deep Dive & Code

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:

<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

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

:

<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

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.

<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

Random

but 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>
mix32

mixes 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.Random

in 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

ThreadLocalRandom

directly; for earlier JDK versions, ensure each thread holds its own Random instance.

JavaPerformanceConcurrencyRandomThreadLocalRandom
Ops Development Stories
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.