Random numbers in Java
Random, ThreadLocalRandom, SecureRandom, RandomGenerator.
nextInt… so where did the randomness go?Pseudo-random
java.util.Random isn't magic: it's an algorithm. A starting number, the seed, fully decides the sequence. Same seed → same sequence — wonderful for reproducible tests and simulations, terrible for secrets.
Twins
What does this print?
Random a = new Random(7);
Random b = new Random(7);
System.out.println(
a.nextInt(1000) == b.nextInt(1000));
System.out.println(
a.nextInt(1000) == b.nextInt(1000));false falsetrue trueIt varies from run to run
Show the answer
true both times — the same seed produces exactly the same sequence, on every run and every machine.
Ranges: origin in, bound out
nextInt(6) gives 0 to 5. **nextInt(origin, bound)** (on Random since Java 17) includes the origin and excludes the bound — so a six-sided die is nextInt(1, 7).
int roll = ThreadLocalRandom.current()
.nextInt(1, 7); // 1..6The family
**Random: general-purpose and seedable. ThreadLocalRandom.current(): fast across many threads, no contention. SecureRandom: unpredictable, for tokens, passwords and keys. Since Java 17 they all implement RandomGenerator**.
A fresh Random every time
Creating new Random(42) inside the loop restarts the same sequence on every iteration, so every roll is identical. Create one Random outside the loop.
int[] counts = new int[6];
for (int i = 0; i < 1000; i++) {
Random r = new Random(42); // bug!
counts[r.nextInt(6)]++;
}Good enough for tokens?
Why not use new Random() to generate password-reset tokens?
Think about it, then reveal the answer
Its output is predictable: after seeing a few values, an attacker can work out the internal state and predict future tokens. Use **SecureRandom**, a cryptographically strong generator.
Randomness in production
Games and simulations use seeded Random so bugs can be replayed. Load balancers and retry jitter use ThreadLocalRandom. Session IDs, reset links and API keys must use SecureRandom — predictable tokens have led to real account takeovers.
Key takeaways
- Same seed → same sequence (handy for tests)
- nextInt(a, b): a inclusive, b exclusive (Java 17+)
- ThreadLocalRandom.current() for concurrent code
- SecureRandom for tokens, passwords and keys
Random's algorithm (a linear congruential generator) is spelled out in its Javadoc, so a seed gives the same numbers on every JVM ever made.
Practice questions
Simulate a six-sided die (1 to 6).
int roll = ThreadLocalRandom.current()
.nextInt(1, ___);- 6
- 7
- 5
Check your answer
7. nextInt(origin, bound) includes the origin but excludes the bound, so you need 7 to make 6 possible.
You need to generate password-reset tokens. Which source of randomness should you use?
- new Random(System.currentTimeMillis())
- Math.random()
- SecureRandom
- ThreadLocalRandom.current()
Check your answer
SecureRandom. Tokens must be unguessable. SecureRandom uses a cryptographically strong generator; the others are predictable.