What are java.util.Random, SecureRandom, and ThreadLocalRandom, and when would you choose each?
answer
- Random = fast + seedable + predictable
- SecureRandom = unpredictable, for tokens/keys/salts
- ThreadLocalRandom = per-thread, no contention
- security->Secure, concurrency->ThreadLocal, else->Random
basics
~20 sRandom is a basic, fast number generator that can repeat if you give it a seed. SecureRandom makes unpredictable numbers for things like passwords and tokens. ThreadLocalRandom is a faster Random for code where many threads run at once.
solid answer
~40 sAll three produce pseudo-random numbers, but they trade off speed, predictability, and concurrency. java.util.Random is a fast, seedable PRNG (linear congruential) good for simulations, games, and tests where reproducibility helps; it is predictable and must not be used for security. SecureRandom is a cryptographically strong generator backed by the OS/JCA entropy sources; use it for tokens, salts, keys, session IDs, and password resets, where an attacker must not predict output. ThreadLocalRandom gives each thread its own generator, eliminating the contention you get when many threads hammer a single shared Random; reach for it in concurrent code (and never share its instance across threads or seed it for reproducibility). Rule of thumb: security -> SecureRandom; concurrency -> ThreadLocalRandom; everything else -> Random.
go deeper
Knows Random makes random numbers and that there's a separate secure one for passwords/tokens.
Can name all three, state the security vs concurrency vs general-purpose split, and pick correctly for a given scenario.
Explains the mechanisms (LCG predictability, contention on shared Random, entropy sources for SecureRandom) and the access patterns (current() factory, don't seed SecureRandom).
Discusses entropy/blocking trade-offs (getInstanceStrong vs new SecureRandom), the Java 17 RandomGenerator unification, and sets team conventions/linting to prevent insecure Random use.
### What 'random' means here Computers cannot produce true randomness from arithmetic. Instead they run a **pseudo-random number generator (PRNG)**: a deterministic function that, starting from an internal number called the **seed**, produces a stream of numbers that *look* random. Same seed in -> same stream out. Java's standard library offers three PRNG-style classes with very different goals. ### 1. java.util.Random The classic, general-purpose PRNG. Internally it is a **linear congruential generator (LCG)** with a 48-bit state, advancing with the formula `state = (state * 0x5DEECE66D + 0xB) mod 2^48`. Key properties: - **Fast and lightweight.** - **Seedable / reproducible:** `new Random(42)` always yields the same sequence. Great for tests, simulations, games, shuffling where you want repeatability. - **Predictable / insecure:** the algorithm and constants are public, and only 48 bits of state. An attacker who sees a couple of outputs can reconstruct the seed and predict all future (and past) values. **Never** use it for anything security-sensitive. - **Thread-safe but contended:** its state is updated with a compare-and-set loop, so when many threads share one `Random`, they fight over the same memory and throughput collapses. ### 2. SecureRandom A **cryptographically strong** PRNG (a CSPRNG). It extends `Random` but overrides the engine with a provider from the **JCA (Java Cryptography Architecture)** that draws from OS entropy (e.g. `/dev/urandom`, Windows CryptGenRandom) and strong algorithms. - **Unpredictable:** designed so that, even seeing many outputs, you cannot predict the next one. This is the whole point. - **Use for:** session IDs, CSRF/password-reset tokens, salts, nonces, key material, anything an attacker would benefit from guessing. - **Cost:** slower than `Random`, and the first call (or `generateSeed`) may briefly block while gathering entropy. Prefer `SecureRandom.getInstanceStrong()` for long-lived keys, or the default constructor `new SecureRandom()` (non-blocking on most platforms) for tokens. - **Don't seed it for reproducibility:** calling `setSeed` to make it deterministic destroys its security guarantee. ### 3. ThreadLocalRandom Introduced in Java 7 to fix the contention problem of a shared `Random` in concurrent code. Each thread gets its **own** generator instance, so there is no shared state and no locking. - **Access via the static factory, never store-and-share:** `ThreadLocalRandom.current().nextInt(bound)`. The instance you get belongs to the calling thread. - **Faster under concurrency** than a shared `Random`, and offers convenient bounded methods like `nextInt(origin, bound)` and `nextDouble(origin, bound)`. - **Not for security** (still a fast statistical PRNG) and **not reproducible** in the usual sense (`setSeed` throws `UnsupportedOperationException`). - **Never share the returned instance across threads** — that reintroduces the exact contention/correctness problem it exists to avoid. ### Decision guide - Need unpredictability for security? -> **SecureRandom**. - Generating randoms from many threads at once? -> **ThreadLocalRandom**. - Single-threaded, want speed and/or reproducibility (tests, sims, games)? -> **Random**. ### Modern note Java 17 added the `RandomGenerator` interface and `RandomGeneratorFactory`, unifying these under one API and adding newer algorithms (e.g. Xoshiro, LXM). The three classes above still implement/relate to it, but the *selection logic by purpose* is unchanged.
- Why is java.util.Random unsafe for security tokens?It is a 48-bit linear congruential generator with public constants; an attacker who observes a few outputs can recover the seed and predict the entire sequence, past and future.
- What happens if you call setSeed on ThreadLocalRandom?It throws UnsupportedOperationException — its design forbids cross-thread reproducible seeding.
saying these in an interview costs you the question
- Using java.util.Random to generate session tokens or passwords
- Sharing one ThreadLocalRandom instance across threads
- Seeding SecureRandom to make it 'reproducible'
- Claiming Random is unsafe only because it's 'not encrypted' (real reason: tiny predictable state)