The JDK does not define a single random-number subsystem. It exposes distinct APIs in java.util, java.util.concurrent, java.security, and, since Java 17, java.util.random. This post gives an overview of those API lineages, then the generator mechanisms behind each type, and finally a lineage selection procedure with invocation patterns. Pseudorandom generation is not encryption; cryptographic protocols obtain secret material from the JCA SecureRandom path.

Related: Cryptographic Primitives: Encoding, Hashing, and Encryption.


1. Overview

OpenJDK provides three lineages. They share only the abstract function of producing bit strings; packages, threat models, and algorithms differ.

LineagePrimary typesFunction
General PRNGjava.util.Random, ThreadLocalRandom, Math.random()Non-cryptographic draws for ordinary application logic
Stream / split PRNGSplittableRandom; splittable RandomGenerator factoriesStatistically independent streams for parallel computation
JCA CSPRNGSecureRandom and registered security providersBits that must remain unpredictable to an adversary

java.util.random.RandomGenerator (Java 17+) standardizes the invocation interface for generators, including optional wrappers around older types. It does not subsume the JCA security boundary and does not remove the pre-existing types already used throughout the ecosystem.

1.1 Historical origin of the lineages

Design pressureResulting API
Seeded PRNG for libraries and Mathjava.util.Random
Contention on a shared Random under concurrencyThreadLocalRandom
Fork-join and parallel streams requiring split streamsSplittableRandom
Provider-based cryptography and operating-system entropySecureRandom (JCA)
Uniform interface over multiple PRNG algorithmsRandomGenerator / RandomGeneratorFactory (Java 17+)

Type inheritance is not a reliable guide to mechanism: ThreadLocalRandom extends Random, yet OpenJDK does not implement it with Random’s 48-bit LCG.

1.2 Guarantees and non-guarantees by lineage

LineageProvidedNot provided
General / stream PRNGDeterministic state transition; statistical quality adequate for simulationUnpredictability under attack; identical algorithm identity for every subtype across all future JDK releases
JCA SecureRandomProvider CSPRNG (operating-system generator and/or NIST DRBG) suitable for secret material when correctly configuredA fixed algorithm name on every platform; non-blocking completion for every getInstanceStrong() resolution

1.3 Type inventory (OpenJDK)

TypeRole
java.util.Random48-bit LCG PRNG
java.util.concurrent.ThreadLocalRandomPer-thread SplitMix-family PRNG
java.util.SplittableRandomSplitMix64 with split / mixGamma
java.security.SecureRandomCSPRNG via JCA providers
java.util.random.RandomGeneratorUniform generator interface (Java 17+)
java.util.random.RandomGeneratorFactoryDiscovery and construction by algorithm name

2. Generator mechanisms

A pseudorandom number generator (PRNG) maintains finite state (S) and a transition (S_{n+1}=f(S_n)); each draw extracts bits from (S) or from a function of (S). A cryptographically secure PRNG (CSPRNG) must remain unpredictable given knowledge of prior outputs. The JDK types differ in (f), state size, and whether adversarial unpredictability is required. The descriptions below follow OpenJDK; algorithm bindings may differ by release and provider configuration.

2.1 Random: 48-bit linear congruential generator

OpenJDK implements Random (and the generator underlying Math.random()) as a linear congruential generator with a 48-bit seed held in an AtomicLong.

State transition (Random.next(int bits)):

[ seed \leftarrow (a \cdot seed + c) \bmod 2^{48} ]

ConstantValueRole
Multiplier (a)0x5DEECE66DLMultiplier in the historical drand48 family
Addend (c)0xBLAdditive constant selected for full period under LCG constraints
Modulus(2^{48})Enforced by masking with ((1L << 48) - 1)

Output extraction. After the transition, the implementation returns the most significant bits of the 48-bit seed:

[ \texttt{next(bits)} = \lfloor seed / 2^{48-\textit{bits}} \rfloor ]

nextInt() requests 32 bits. nextLong() concatenates two 32-bit results. nextDouble() constructs a value in ([0,1)) from 53 bits of significand material.

Seeding. The constructor and setSeed transform the caller-supplied seed by XOR with the multiplier and reduction to 48 bits, so nearby user seeds are not adjacent in the LCG orbit. The transformation does not confer cryptographic strength.

Properties.

  • Maximum period (2^{48}).
  • The sequence is determined by the 48-bit seed; sufficiently many observed outputs permit state recovery.
  • Concurrent use of a single instance synchronizes within next, which motivates ThreadLocalRandom on contended paths.

2.2 ThreadLocalRandom: per-thread SplitMix-family generator

Although ThreadLocalRandom subclasses Random, modern OpenJDK does not use the 48-bit LCG. The implementation follows the SplitMix family used by SplittableRandom: per-thread state, without a public split() operation.

Per-thread state. Each Thread stores threadLocalRandomSeed. A process-wide seeder derives distinct initial seeds from currentTimeMillis and nanoTime, or, when java.util.secureRandomSeed is true, from eight bytes obtained via SecureRandom.getSeed.

State advance (Weyl sequence). Each draw adds an odd increment so that the walk has maximal period over 64-bit words. OpenJDK uses:

[ seed \leftarrow seed + \bigl(2\cdot\textit{threadId} + \gamma\bigr) ]

with

[ \gamma = \mathtt{GOLDEN_GAMMA} = \mathtt{0x9e3779b97f4a7c15L} \approx 2^{64}/\varphi. ]

The thread identifier term yields an odd, thread-distinct step while retaining bit transitions contributed by (\gamma).

Output mixing. The advanced seed is not emitted directly. It is passed through a 64-bit avalanche function (\mathrm{mix64}) (Stafford / Murmur-style finalizer). nextInt and nextLong take bits from the mixed value, suppressing the linear structure of the additive walk for ordinary statistical tests.

Properties.

  • Documented period (2^{64}) for the additive component.
  • The common path is unsynchronized; callers must invoke ThreadLocalRandom.current() on the thread that consumes the values.
  • The generator is not a CSPRNG and must not supply keys or tokens.

2.3 SplittableRandom: SplitMix64

SplittableRandom is OpenJDK’s SplitMix64 generator for fork-join and parallel streams.

State. Two 64-bit fields:

FieldMeaning
seedPosition on an arithmetic progression
gammaOdd Weyl step size; must remain odd

The default gamma for an unsplit instance is (\mathtt{GOLDEN_GAMMA} = \mathtt{0x9e3779b97f4a7c15L}).

Next output.

  1. (seed \leftarrow seed + gamma)
  2. (z \leftarrow \mathrm{mix64}(seed))
  3. Return selected bits of (z)

OpenJDK’s mix64 is Stafford variant 13:

z ^= z >>> 30;  z *= 0xbf58476d1ce4e5b9L;
z ^= z >>> 27;  z *= 0x94d049bb133111ebL;
z ^= z >>> 31;
return z;

mix32 performs the corresponding extraction for nextInt.

split(). Constructs a child generator intended to avoid statistical collision with the parent:

  1. Obtain mixed values from the parent for the child’s initial seed and a candidate gamma.
  2. Derive child gamma with mixGamma: MurmurHash3 64-bit mix constants, force odd parity (| 1), and if the number of 01/10 transitions is below 24 (Long.bitCount(z ^ (z >>> 1)) < 24), XOR with 0xaaaaaaaaaaaaaaaaL.

Each child therefore receives a distinct gamma (a distinct Weyl stride), which is the essential property of the Steele, Lea, and Flood splittable construction.

Properties.

  • Deterministic given (seed, gamma).
  • Suitable for Monte Carlo workloads that require independent streams; not a CSPRNG.

2.4 SecureRandom: provider-backed CSRNGs

SecureRandom is a JCA engine. The algorithm name selects a SecureRandomSpi from a registered provider. The Java language specification does not prescribe a unique PRNG algorithm.

NativePRNG and NativePRNGBlocking on Linux

  • NativePRNG reads from /dev/urandom (non-blocking kernel entropy pool). Some JDK releases additionally maintain a Java-side digest for setSeed and supplemental input; the entropy source remains the operating-system CSPRNG.
  • NativePRNGBlocking may read /dev/random, which can block when the kernel entropy estimate is insufficient.

Modern Linux kernels typically implement the CSPRNG with ChaCha20; earlier kernels used other constructions. From the Java caller’s perspective, nextBytes returns operating-system-backed unpredictable output.

Windows-PRNG

Delegates to the Windows cryptographic RNG (CryptGenRandom or BCryptGenRandom, depending on JDK and provider).

DRBG under NIST SP 800-90A

A deterministic random bit generator constructed from an approved primitive and reseeded from entropy:

MechanismConstruction
Hash_DRBGState updated with a hash function (for example SHA-256); output derived by hashing state
HMAC_DRBGState updated with HMAC; output derived from an HMAC chain
CTR_DRBGState comprises a key and counter; output from a block cipher in counter mode (for example AES-CTR)

Configuration through securerandom.drbg.config or SecureRandom.getInstance("DRBG", params) selects the mechanism, security strength (for example 128, 192, or 256 bits), prediction resistance, and optional personalization. When so configured, the DRBG reseeds from entropy after producing a bounded volume of output.

SHA1PRNG (legacy Sun provider)

A Java-implemented generator based on SHA-1: internal digest state yields each output block; state is then updated, typically by incorporating prior output. Prefer an operating-system CSPRNG or an SP 800-90A DRBG for new deployments.

SecureRandom.getInstanceStrong() resolves securerandom.strongAlgorithms in java.security (for example NativePRNGBlocking:SUN,DRBG:SUN) and returns the first available algorithm. Blocking native algorithms may impose latency; selection must match operational constraints.

2.5 RandomGenerator algorithm families (Java 17+)

RandomGenerator and RandomGeneratorFactory expose named algorithms. Non-cryptographic factories share large state, an efficient transition, and optional leap or split operations.

LXM family

The Steele and Vigna LXM construction combines, at each step:

  1. L — an LCG of width (p) (for example 64 bits): (s \leftarrow a\cdot s + c \bmod 2^{p})
  2. X — an xor-based generator (XBG) of width (q) (for example 128-bit xoroshiro-like state)
  3. M — a mixer that combines the L and X contributions into the returned value

The name L64X128MixRandom denotes a 64-bit LCG, a 128-bit XBG, and a mixer. For full-period parameters the period is on the order of (2^{p+q}). Selected LXM instances support split or jump for parallel streams.

Xoroshiro and Xoshiro families

State comprises several long words updated by rotates, shifts, and exclusive-or operations (Blackman and Vigna). The PlusPlus output function applies addition, rotation, and addition to state words. Periods are (2^{128}-1) or (2^{256}-1), excluding the all-zero state. These generators are not CSRNGs.

Factory wrappers of earlier types

Factory nameMechanism
Random48-bit LCG (§2.1)
SplittableRandomSplitMix64 (§2.3)
SecureRandomProvider CSPRNG (§2.4)

2.6 Algorithm comparison by API

APICore algorithmApproximate stateCSPRNG
Random48-bit LCG48 bitsNo
ThreadLocalRandomSplitMix-family (per-thread seed and mix64)64-bit seed per threadNo
SplittableRandomSplitMix64 with mixGamma on splitseed and gamma (~128 bits)No
LXM / Xoroshiro factoriesLCG+XBG+mix, or xor/rotate permutations128–256+ bitsNo
SecureRandomOperating-system CSPRNG and/or NIST DRBG; legacy SHA1PRNGProvider-definedYes, when a suitable provider algorithm is used

3. Lineage selection

After the mechanisms above, selection is a mapping from requirements onto lineages. Evaluate requirements in order; do not choose a type by method convenience alone.

Java random lineage selection

3.1 Selection by requirement

  • Cryptographic secret material (keys, tokens, salts, IVs, nonces): SecureRandom exclusively.
  • Non-cryptographic draws under concurrency: ThreadLocalRandom.current().
  • Non-cryptographic draws with a prescribed seed and reproducible sequence: a constructed Random (or other seedable generator).
  • Independent streams for parallel or fork-join tasks: SplittableRandom.split() or a splittable RandomGenerator.
  • Selection of a named non-cryptographic algorithm (Java 17+): RandomGeneratorFactory.

3.2 Selection procedure

  1. Adversarial unpredictability. If an adversary must not be able to predict subsequent outputs from prior outputs or public state, select the JCA lineage (SecureRandom) and terminate the procedure.
  2. Stream independence. If distinct tasks require streams that are intended to be statistically independent (for example parallel Monte Carlo with split), select the stream / split lineage.
  3. Otherwise select the general PRNG lineage. Prefer ThreadLocalRandom when no explicit seed is required. Construct a seeded Random, SplittableRandom, or seedable RandomGenerator when bit-identical reproducibility is required.
RequirementLineageRepresentative type
Adversarial unpredictabilityJCASecureRandom
Concurrent non-cryptographic drawsGeneral PRNGThreadLocalRandom
Seeded reproducibilityGeneral or stream PRNGSeeded Random / SplittableRandom
Independent parallel streamsStream / splitSplittableRandom, LXM factories
Named algorithm catalog (Java 17+)Stream / split (and wrappers)RandomGeneratorFactory

Incorrect lineage selection includes using Random for authentication tokens, and using SecureRandom for high-volume simulation when unpredictability is not required (correct for security, often disproportionate in cost).

3.3 Non-cryptographic generation

import java.util.concurrent.ThreadLocalRandom;

int n = ThreadLocalRandom.current().nextInt(100);       // [0, 100)
long id = ThreadLocalRandom.current().nextLong();
double u = ThreadLocalRandom.current().nextDouble();    // [0.0, 1.0)

Seeded Random when a reproducible sequence is required:

import java.util.Random;

Random r = new Random(42L);
int a = r.nextInt(10);
int b = r.nextInt(10);

3.4 Splittable stream construction

import java.util.SplittableRandom;
import java.util.stream.IntStream;

SplittableRandom root = new SplittableRandom(12345L);
IntStream.range(0, 4).parallel().forEach(i -> {
    SplittableRandom local = root.split();
    System.out.println(local.nextInt());
});

3.5 Cryptographic byte generation

import java.security.SecureRandom;

SecureRandom sr = new SecureRandom();
// SecureRandom sr = SecureRandom.getInstanceStrong();

byte[] key = new byte[16];    // e.g. AES-128 key material
sr.nextBytes(key);

byte[] token = new byte[32];
sr.nextBytes(token);

Do not derive tokens from Random.nextInt encodings. Do not initialize SecureRandom for production secrets with low-entropy material such as wall-clock time or process identifiers alone.

3.6 Named RandomGenerator construction (Java 17+)

import java.util.random.RandomGenerator;
import java.util.random.RandomGeneratorFactory;

RandomGenerator g = RandomGeneratorFactory.of("L64X128MixRandom").create();
long x = g.nextLong();

RandomGeneratorFactory.all()
    .map(RandomGeneratorFactory::name)
    .sorted()
    .forEach(System.out::println);

TopicSummary
OverviewThree API lineages; historical origin; guarantees; type inventory
Generator mechanismsLCG; SplitMix (ThreadLocalRandom, SplittableRandom); LXM / Xoroshiro; OS CSPRNG and NIST DRBG
Lineage selectionRequirement mapping, selection procedure, then invocation patterns