Java ThreadLocal Explained: How ThreadLocal Works Internally
ThreadLocal provides thread-local variables. Each thread accessing a ThreadLocal variable maintains an independent copy of the variable, preventing issues caused by shared mutable state across threads.
What is ThreadLocal in Java?
ThreadLocal provides get and set accessor methods that maintain a separate copy of the value for each thread that uses it, so a get returns the most recent value passed to set from the currently executing thread.
This design helps isolate state per thread, commonly used in scenarios such as database connection handling.
1. How ThreadLocal Works Internally
1.1 set() Method
public void set(T value) {
// Get the current thread
Thread t = Thread.currentThread();
// Retrieve the ThreadLocalMap associated with the current thread
ThreadLocalMap map = getMap(t);
// If the map exists, store the value using the current ThreadLocal as the key
if (map != null)
map.set(this, value);
else
// Otherwise, create a new map
createMap(t, value);
}
1.2 get() Method
public T get() {
// Get the current thread
Thread t = Thread.currentThread();
// Retrieve the ThreadLocalMap associated with the current thread
ThreadLocalMap map = getMap(t);
// If the map is not null,
// look up the Entry using the current ThreadLocal as the key and return the value
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// If not found, initialize the value (returns null by default)
return setInitialValue();
}
From the above set and get method implementations,
- The data structure used to store values—
ThreadLocalMap—belongs to theThreadobject itself. - The key in the
ThreadLocalMapis the currentThreadLocalinstance.
The ThreadLocal instance, when calling its own set() or get() method, uses itself as the key, and stores the corresponding value in the ThreadLocalMap of the current thread. Furthermore, the ThreadLocalMap stores both the key and the value in an Entry structure.
2. Thread’s ThreadLocalMap structure
set() Method of ThreadLocalMap
The set() method stores the key-value pair in an Entry structure and places it into the internal array table. It uses the hash value of the ThreadLocal object to determine the appropriate bucket index.
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
// Calculate the hash to find the appropriate bucket
int i = key.threadLocalHashCode & (len - 1);
// Traverse the array using linear probing
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
// If the key already exists, replace the value
if (k == key) {
e.value = value;
return;
}
// If the key has been garbage collected (null), replace the stale entry
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
// Otherwise, insert the new Entry at the empty slot
tab[i] = new Entry(key, value);
int sz = ++size;
// Resize if necessary
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
Explanation
- The
keyandvalueare wrapped in an Entry structure. ThreadLocalMapis implemented as an array ofEntryobjects.- It uses open addressing with linear probing to resolve hash collisions.
- If the same key already exists, it updates the value.
- If it finds a stale entry (whose key is null), it reuses the slot.
- Otherwise, it continues probing to find the next available slot.
- When the number of entries exceeds the threshold, the table is resized.
3. WeakReference in ThreadLocalMap.Entry
The Entry class extends WeakReference<ThreadLocal<?>> and is a static inner class within ThreadLocalMap. In the constructor, the ThreadLocal key is stored as a weak reference.
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
...
}
Explanation
- A weak reference does not prevent its referent (in this case, the
ThreadLocalobject) from being garbage collected. - This design ensures that if a
ThreadLocalkey becomes unreachable (i.e., has no strong references pointing to it), it can be garbage collected, even though the associated value is still stored in the map. - The use of a weak reference helps prevent memory leaks caused by threads holding on to
ThreadLocalvalues longer than necessary. - When the
ThreadLocalkey is reclaimed, theThreadLocalMapwill eventually detect that the key isnulland clean up the corresponding entry using thereplaceStaleEntry()method. - However, if the
ThreadLocalinstance is still strongly referenced elsewhere, it will not be collected, and the map remains accessible.
This weak reference mechanism is a key part of the memory management strategy in ThreadLocalMap, balancing usability with garbage collection safety.