Mastering Java ThreadLocal: Core Operations, Map Structure, and Practical Examples
This article explains Java's ThreadLocal mechanism, detailing its internal ThreadLocalMap structure, core set/get/remove operations with full code examples, and demonstrates how multiple ThreadLocal variables are stored per thread, providing a clear understanding of thread‑local storage in backend development.
ThreadLocalclass contains a static inner class ThreadLocalMap, which implements the thread‑isolation mechanism. Each Thread has a ThreadLocalMap instance.
public class Thread implements Runnable {
// ThreadLocal variable stored in Thread class
ThreadLocal.ThreadLocalMap threadLocals = null;
}ThreadLocal Core Operations
set operation
public void set(T value) {
// Get current thread
Thread t = Thread.currentThread();
// Get current thread's ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// If map exists, set value
map.set(this, value);
} else {
// If map does not exist, create map and set value
createMap(t, value);
}
}
// ThreadLocalMap's set method
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
// Compute index using ThreadLocal's hashCode
int i = key.threadLocalHashCode & (len - 1);
// Handle hash collision, linear probing
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
// If same key, update value
if (k == key) {
e.value = value;
return;
}
// If key is null, it has been GC'd, replace entry
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
// Create new Entry
tab[i] = new Entry(key, value);
size++;
// Check if resize needed
if (!cleanSomeSlots(i, size) && size >= threshold)
rehash();
}get operation
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T) e.value;
return result;
}
}
return setInitialValue();
}
// ThreadLocalMap's getEntry method
private Entry getEntry(ThreadLocal<?> key) {
// Compute index
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
// If found and key matches, return
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}remove operation
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
// ThreadLocalMap's remove method
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len - 1);
// Find and delete Entry
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
// Clear reference
e.clear();
// Clean up stale entry
expungeStaleEntry(i);
return;
}
}
}It can be seen that ThreadLocal's get, set, and remove methods ultimately operate on the data stored in ThreadLocalMap.
ThreadLocalMap Structure
ThreadLocalMap contains a static inner class Entry that extends WeakReference<ThreadLocal?>. The key is a weak reference, while the value is a strong reference.
static class ThreadLocalMap {
// Entry extends WeakReference, key is weak reference
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value; // value is strong reference
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold;
...
}Summary
ThreadLocal stores data in a Thread's ThreadLocalMap. ThreadLocalMap holds an Entry array that can store multiple ThreadLocal objects; a single thread can hold several ThreadLocal values via the same ThreadLocalMap. Each ThreadLocal object caches only one variable value.
One thread can set multiple ThreadLocal variables:
public class ThreadLocalTest {
// Define multiple ThreadLocal objects
private static final ThreadLocal<String> nameThreadLocal = new ThreadLocal<>();
private static final ThreadLocal<Integer> ageThreadLocal = new ThreadLocal<>();
private static final ThreadLocal<User> userInfoThreadLocal = new ThreadLocal<>();
public void setVal() {
// Same thread can set multiple ThreadLocal values
nameThreadLocal.set("xuanwu");
ageThreadLocal.set(18);
userInfoThreadLocal.set(new User("xuanwu", 18));
// Get each ThreadLocal value
String name = nameThreadLocal.get();
Integer age = ageThreadLocal.get();
User user = userInfoThreadLocal.get();
}
}Xuanwu Backend Tech Stack
Primarily covers fundamental Java concepts, mainstream frameworks, deep dives into underlying principles, and JVM internals.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
