Concurrencymedium

ThreadLocal

ThreadLocal provides thread-confined storage — each thread that accesses a ThreadLocal variable gets its own independent copy. Changes in one thread are not visible to others.

Memory anchor

ThreadLocal = a personal locker at a gym. Each thread gets its own locker. But in a thread pool, threads are reused like rental lockers -- if you don't clean yours out, the next renter finds your sweaty towel.

Expected depth

Common use cases: per-thread SimpleDateFormat (which is not thread-safe), per-thread database connections, Spring's SecurityContextHolder and TransactionSynchronizationManager. Implemented as a map inside each Thread object (Thread.threadLocals — a ThreadLocalMap). InheritableThreadLocal propagates values to child threads at fork time (but not dynamically after that — not suitable for async handoff). ThreadLocal.withInitial() takes a Supplier for lazy initialization.

Deep — senior internals

Thread pool memory leak: if a thread in a pool calls ThreadLocal.set() but never calls remove(), the value persists for the lifetime of that thread. In a servlet container running on a thread pool, this means per-request data (e.g., user identity, database connections) can leak between requests. The fix: always call remove() in a finally block (or use a request-scoped Spring bean). ThreadLocalMap uses WeakReference keys (the ThreadLocal itself) so if the ThreadLocal variable goes out of scope, its entry can be GC'd — but the value (a strong reference) stays until the thread dies or the entry is cleaned. Virtual threads (Java 21) are cheap enough that a new virtual thread per request eliminates the need for ThreadLocal for many use cases.

🎤Interview-ready answer

ThreadLocal gives each thread its own slot for a value — used for per-thread caching of non-thread-safe objects (e.g., SimpleDateFormat) or passing context implicitly down a call stack without method parameters (Spring's TransactionSynchronizationManager). The critical operational rule: always call ThreadLocal.remove() when done, especially in thread pools, to prevent memory leaks and cross-request data contamination. ThreadLocalMap's WeakReference key prevents leak of the ThreadLocal itself, but not of the value.

Common trap

In a thread pool, failing to call ThreadLocal.remove() means the next request handled by that thread inherits the previous request's data — a security and correctness bug that only manifests under load when threads are reused.