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:

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;
}
mix32

mixes 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: 203882

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.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

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

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.