Thoughts on ThreadLocal

2 main usages of java.lang.ThreadLocal variables:

  1. Vertical sharing: sharing and propagation of contextual data through the entire processing cycle. This is similar to http request attributes binding, except for the different scopes. A ThreadLocal variable (e.g., security context, transaction context) can be propagated across different tiers, e.g., from web to local EJB. After the processing is done, care must be taken to clear ThreadLocal variables to avoid context leaking.

  2. Horizontal sharing: sharing of objects across repeated and concurrent processing tasks. It offers a third approach for achieving thread safety, besides object locking and unshared instances. No need to clear TheadLocal variables after the current processing is done, since they are intended to be shared throughout the life of the thread.
However, when different class loaders are used among those tasks, the ThreadLocal object previously stored is not visible to the new class loader, and a new binding will be created. So over time, stales entries (instances of ThreadLocal data, and possibly anonymous inner subclass of ThreadLocal) will continue to consume memory. On the bright side, ThreadLocalMap keys use WeakReference and will eventually be cleared when the map approaches its capacity.

Think of ThreadLocal as a container that holds thread-sensitive application data. Therefore, a ThreadLocal variable can be associated with multiple threads. A thread can be associated with multiple ThreadLocal variables.

Thread class maintains a threadLocals Map (ThreadLocal.ThreadLocalMap) to hold all ThreadLocal variables for the current thread. The map key is the ThreadLocal instance, and the value is the actual object for this thread. When you call aThreadLocal.get() to retrieve the current binding, the get() method just asks the current thread to look up in its threadLocals map, using itself (aThreadLocal) as the key.

So the per-thread data is internally stored inside Thread class. Why did they create this ThreadLocal mediator class sitting in the middle? If I were to implement it, my first thought would be to introduce new methods to Thread class, such as setContextData(key, val), getContextData(key) & removeContextData(key).

Here are some reasons I can think of why ThreadLocal was designed this way:

1, Division of labor. Thread as a low-level construct should not be directly manipulated by applications. Application state is better managed by applcation classes.

2, Type safety. ThreadLocal<T> as a generic type is type-safe, and you can declare it to be ThreadLocal<Integer>, ThreadLocal<String>, or ThreadLocal<MyType>.

3, Encapsulation. The recommended practice is to declare a ThreadLocal<T> variable to be

private static final ThreadLocal<MyType> xxx = new ThreadLocal<MyType>();

The applicaton class can choose to completely hide the use of ThreadLocal as an implementation detail, or selectively allow certain operations, e.g., getXXX, setXXX, or removeXXX.