Sweaty Imposter

Thread-Local Storage

TechniqueConcurrencyMidSeniorUpdated
Per-thread variable: every thread sees its own copy, eliminating sharing and therefore eliminating the need to synchronize.

When to Use Thread-Local Storage

Non-thread-safe objects you want to reuse per thread (SimpleDateFormat, Random, request-scoped context). Cheaper than allocating per call, safer than sharing.

In Practice

Java's ThreadLocal<T> / InheritableThreadLocal powers SLF4J's MDC, Spring's RequestContextHolder, and Hibernate's session-per-thread. JDK's ThreadLocalRandom avoids contention on the global Random seed.

Sketch

Java

private static final ThreadLocal<SimpleDateFormat> ISO =
    ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"));

public String format(Date d) {
    return ISO.get().format(d);
}

How Thread-Local Storage Comes Up in Interviews

Scenario

Java's SimpleDateFormat is not thread-safe but allocating one per request shows up in your profiler. You don't want a lock around it either. What's the idiomatic JDK answer?

ThreadLocal.withInitial(...) gives each thread its own formatter: zero sharing, zero locking, allocated once per thread. The modern replacement (DateTimeFormatter) is immutable, which would be the cleanest answer, but ThreadLocal is the technique you're naming.

Scenario

A Python web server runs as one process with a pool of worker threads, one request per thread. A helper saves the current user in a module-level variable so deeper functions can read it. Under load, some requests act on another user's account. Each thread needs its own copy of that value. What do you use?

Threads in one process share its memory, globals included, so one request overwrites the user that another request is still using. Thread-local storage (threading.local in Python) gives each thread its own value under the same name. A mutex could only make this safe by holding the lock for the entire request, which runs one request at a time.

A process has its own memory, and two processes share nothing unless they set it up through pipes, sockets, or shared memory. Threads run inside one process and share its heap, globals, and open files, while each keeps its own stack, which makes them cheaper to start and is why shared state needs locks or per-thread copies. In a thread pool, clear thread-local values when each request ends, or the next request on that thread inherits them.