What are use cases of ThreadLocal variables in Java?

ThreadLocal provides each thread its own independent copy of a variable, so multiple threads can use what looks like a shared variable without any synchronization, because no thread ever sees another thread's value.

Key Points: • Each thread accessing a ThreadLocal gets its own isolated value, initialized independently and invisible to other threads. • A common use case is storing per-request context in web applications — such as the current user, locale, or transaction ID — accessible from anywhere in the call stack without passing it as a parameter everywhere. • Database frameworks often use ThreadLocal to bind a Connection or Session to the thread handling the current request (as Hibernate's CurrentSessionContext does). • DateFormat and other notoriously non-thread-safe classes were historically wrapped in ThreadLocal so each thread got its own instance instead of sharing one unsafely. • ThreadLocal values must be explicitly removed (remove()) when a thread is returned to a pool, or stale data and memory leaks can occur since pooled threads are reused across requests.

Example: A web framework can store the currently authenticated user in a ThreadLocal<User> set by a filter at the start of each request, letting any downstream service or utility method access SecurityContext.getCurrentUser() without threading that value through every method signature.

Code Example:

private static final ThreadLocal<String> currentUser = new ThreadLocal<>();

public static void setUser(String user) {
    currentUser.set(user);
}

public static String getUser() {
    return currentUser.get();
}

public static void clear() {
    currentUser.remove(); // essential when using thread pools
}

Interview Tip: A concise interview answer is:

"ThreadLocal gives each thread its own isolated copy of a variable, which is great for per-request context like the current user or a bound database session in a web application, without needing synchronization. The catch is you have to explicitly call remove() when done, especially in pooled-thread environments, or you risk stale data leaking into the next request."