How do you use ThreadLocalRandom correctly, and what problem does it solve compared to a shared Random?
answer
- current() at point of use, use immediately
- never store/share the instance across threads
- fixes CAS contention + cache-line bouncing
- setSeed throws -> not reproducible
- bounded overloads: nextInt(origin, bound)
basics
~20 sCall ThreadLocalRandom.current() each time you need it and use the result right away; never save it and pass it to other threads. It avoids the slowdown you get when many threads share one Random object.
solid answer
~40 sA single Random shared by many threads becomes a bottleneck: every call updates the same internal state via a compare-and-set retry loop, so threads contend and throughput drops under load. ThreadLocalRandom gives each thread its own generator. You don't construct it — you call the static factory ThreadLocalRandom.current(), which returns the calling thread's instance, and you use it immediately, e.g. ThreadLocalRandom.current().nextInt(0, 100). The two key rules: never share the returned instance across threads (that reintroduces contention and correctness issues), and don't rely on seeding it for reproducibility — setSeed throws UnsupportedOperationException. It also offers handy bounded overloads like nextInt(origin, bound) and nextDouble(origin, bound). It's a fast statistical PRNG, so it's not for security.
go deeper
Knows ThreadLocalRandom is a 'faster Random for threads' and uses current().
Uses current() correctly, knows not to share the instance, and can state it solves contention vs a shared Random.
Explains the CAS/cache-line contention mechanism, the no-reproducible-seed constraint, and chooses it appropriately vs Random/SecureRandom.
Sets concurrency conventions (jitter/backoff sampling), reasons about scalability under core count, and the Java 17 RandomGenerator unification.
### The problem: contention on a shared Random `java.util.Random` is thread-safe: its 48-bit state is updated with an atomic **compare-and-set (CAS)** loop. CAS means 'read the current value, compute the next, and only write it back if no one else changed it in the meantime; otherwise retry'. With one thread that's fine. With many threads all calling the *same* `Random`, they constantly invalidate each other's CAS attempts and retry, and they all touch the **same cache line**, causing cache-coherency traffic between CPU cores. The result: as you add threads, random-number throughput stalls instead of scaling. ### The fix: a generator per thread `ThreadLocalRandom` (Java 7+) eliminates sharing. Conceptually each thread carries its **own** PRNG state, so there is no shared memory to contend on and no locking. That's why it scales with cores. ### How to use it correctly ```java int dice = ThreadLocalRandom.current().nextInt(1, 7); // 1..6 double u = ThreadLocalRandom.current().nextDouble(); // [0.0, 1.0) ``` Rules that follow from its design: - **Always obtain it via `ThreadLocalRandom.current()`** at the point of use. The returned object is *the current thread's* instance. - **Never store the instance in a field or pass it to another thread.** If thread B uses thread A's instance, you lose the per-thread isolation — you get contention back *and* potential incorrectness. (This is the single most common mistake.) - **It isn't reproducible:** `setSeed(...)` throws `UnsupportedOperationException`. If you need a fixed seed for tests, use `Random` instead. - **Bounded helpers:** `nextInt(origin, bound)`, `nextLong(origin, bound)`, `nextDouble(origin, bound)` give half-open ranges `[origin, bound)` without manual modulo bias handling. - **Not for security:** it's a fast statistical PRNG, not a CSPRNG. Use `SecureRandom` for tokens/keys. ### Where it fits Use `ThreadLocalRandom` in any concurrent workload that needs random numbers — parallel simulations, load generators, randomized backoff/jitter in retry logic, sampling in concurrent data structures. For single-threaded code, plain `Random` is fine; the contention problem doesn't exist there. ### Modern note Since Java 17, all these generators implement the `RandomGenerator` interface; `ThreadLocalRandom.current()` still returns the per-thread generator, and the usage rules above are unchanged.
- Why is sharing the instance returned by current() across threads wrong?It defeats the per-thread design — you reintroduce shared-state contention and risk incorrect/biased results. Each thread must call current() to get its own generator.
- What does nextInt(10, 20) return?A uniformly random int in the half-open range [10, 20) — i.e. 10 through 19 inclusive.
saying these in an interview costs you the question
- Caching ThreadLocalRandom.current() in a field/singleton and reusing it from many threads
- Trying to seed it for deterministic tests
- Using it for security tokens
- Thinking a shared Random is fine at high concurrency because it's 'thread-safe'