Fundamentals 15 min read

Mastering Java Random Number Generation: Random, ThreadLocalRandom, SecureRandom & Math

This article reviews four Java random number generation techniques—Random, ThreadLocalRandom, SecureRandom, and Math.random()—detailing their usage, underlying algorithms, thread‑safety, performance trade‑offs, and appropriate scenarios, helping developers choose the right tool for their applications.

macrozheng
macrozheng
macrozheng
Mastering Java Random Number Generation: Random, ThreadLocalRandom, SecureRandom & Math

1. Random

Random class was introduced in JDK 1.0. It generates pseudo‑random numbers using a linear congruential generator (LGC). The algorithm starts from a seed value; the same seed produces the same sequence. By default, new Random() uses the current nanosecond time as the seed.

① Basic usage

<code>// Create Random object
Random random = new Random();
for (int i = 0; i < 10; i++) {
    // Generate 0‑9 random integer
    int number = random.nextInt(10);
    System.out.println("Generated random number: " + number);
}
</code>

Sample output:

Random output
Random output

② Advantages and disadvantages

Random’s LGC algorithm offers high execution efficiency and fast generation speed.

However, if two Random instances share the same seed, they produce identical, predictable sequences, as shown in the following multithreaded example:

<code>// Create two threads
for (int i = 0; i < 2; i++) {
    new Thread(() -> {
        // Create Random with same seed
        Random random = new Random(1024);
        // Generate 3 random numbers
        for (int j = 0; j < 3; j++) {
            int number = random.nextInt();
            System.out.println(Thread.currentThread().getName() + ":" + number);
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("---------------------");
        }
    }).start();
}
</code>

Result:

Random multithread output
Random multithread output

③ Thread‑safety

Random is thread‑safe; its implementation uses CAS (Compare‑And‑Swap) to update the seed atomically.

<code>public Random() {
    this(seedUniquifier() ^ System.nanoTime());
}
public int nextInt() {
    return next(32);
}
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));
}
</code>

Because Random relies on CAS, it performs well for most use cases, but heavy contention can degrade performance.

2. ThreadLocalRandom

ThreadLocalRandom was added in JDK 1.7 as part of java.util.concurrent. It gives each thread its own seed, avoiding contention on a shared seed and improving performance in highly concurrent scenarios.

① Basic usage

<code>// Obtain ThreadLocalRandom instance
ThreadLocalRandom random = ThreadLocalRandom.current();
for (int i = 0; i < 10; i++) {
    // Generate 0‑9 random integer
    int number = random.nextInt(10);
    System.out.println("Generated random number: " + number);
}
</code>

Sample output:

ThreadLocalRandom output
ThreadLocalRandom output

② Implementation principle

ThreadLocalRandom stores a per‑thread seed that is updated by adding a constant (GAMMA) and writing back with Unsafe. The seed is mixed to produce the random value.

<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;
}
final long nextSeed() {
    Thread t; long r;
    UNSAFE.putLong(t = Thread.currentThread(), SEED,
                   r = UNSAFE.getLong(t, SEED) + GAMMA);
    return r;
}
</code>

③ Advantages and disadvantages

ThreadLocalRandom combines the speed of Random with per‑thread isolation, delivering better performance under contention while remaining thread‑safe.

Unlike Random, it does not support setting a seed; calling setSeed throws UnsupportedOperationException, reducing the chance of duplicate sequences across threads.

<code>public void setSeed(long seed) {
    // only allow call from super() constructor
    if (initialized)
        throw new UnsupportedOperationException();
}
</code>

Calling setSeed results in an exception, as shown below:

setSeed exception
setSeed exception

ThreadLocalRandom drawbacks

Although it does not allow manual seed setting, its default seed is derived from the current time. If the JVM is started with -Djava.util.secureRandomSeed=true, a more unpredictable seed is used; otherwise, threads started at the same time may generate identical sequences.

<code>private static long initialSeed() {
    // Try to get JVM start parameter
    String sec = VM.getSavedProperty("java.util.secureRandomSeed");
    if (Boolean.parseBoolean(sec)) {
        byte[] seedBytes = java.security.SecureRandom.getSeed(8);
        long s = (long)(seedBytes[0]) & 0xffL;
        for (int i = 1; i < 8; ++i)
            s = (s << 8) | ((long)(seedBytes[i]) & 0xffL);
        return s;
    }
    // Fallback to time‑based seed
    return (mix64(System.currentTimeMillis()) ^
            mix64(System.nanoTime()));
}
</code>

3. SecureRandom

SecureRandom extends Random and provides a cryptographically strong random number generator. It gathers entropy from sources such as mouse movements and keyboard events, making its seed unpredictable.

Basic usage

<code>// Create SecureRandom with specific algorithm
SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
for (int i = 0; i < 10; i++) {
    int number = random.nextInt(10);
    System.out.println("Generated random number: " + number);
}
</code>

Sample output:

SecureRandom output
SecureRandom output

SecureRandom supports two default algorithms:

SHA1PRNG provided by sun.security.provider.SecureRandom

NativePRNG provided by sun.security.provider.NativePRNG

You can also instantiate it with

new SecureRandom()

, which uses NativePRNG by default, or specify an algorithm via

SecureRandom.getInstance("algorithmName")

. JVM launch parameters can change the algorithm.

4. Math

The Math class, also introduced in JDK 1.0, offers the static method

Math.random()

, which returns a double between 0 (inclusive) and 1 (exclusive). Internally it creates a singleton Random instance.

① Basic usage

<code>for (int i = 0; i < 10; i++) {
    double number = Math.random();
    System.out.println("Generated random number: " + number);
}
</code>

Sample output:

Math.random output
Math.random output

② Extended usage

To generate an int within a specific range, multiply the double by the range size and cast to int:

<code>for (int i = 0; i < 10; i++) {
    // Generate an integer from 0‑99
    int number = (int) (Math.random() * 100);
    System.out.println("Generated random number: " + number);
}
</code>

Sample output:

Math.random range output
Math.random range output

③ Implementation principle

When

Math.random()

is first called, it lazily creates a new

java.util.Random

instance and reuses it for subsequent calls.

<code>public static double random() {
    return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
private static final class RandomNumberGeneratorHolder {
    static final Random randomNumberGenerator = new Random();
}
</code>

Summary

This article covered four ways to generate random numbers in Java. Random provides fast pseudo‑random numbers but can suffer from contention under heavy multithreading. ThreadLocalRandom eliminates that contention and is suitable for highly concurrent scenarios. SecureRandom offers cryptographically strong randomness for security‑critical use cases. Math.random() is a thin wrapper around Random and shares its characteristics.

JavaConcurrencyThreadLocalRandomMath.randomRandom Number GenerationSecureRandom
macrozheng
Written by

macrozheng

Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.

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.