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.

Xuanwu Backend Tech Stack
Xuanwu Backend Tech Stack
Xuanwu Backend Tech Stack
Mastering Java ThreadLocal: Core Operations, Map Structure, and Practical Examples
ThreadLocal

class 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();
    }
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend DevelopmentconcurrencyThreadLocalThreadLocalMap
Xuanwu Backend Tech Stack
Written by

Xuanwu Backend Tech Stack

Primarily covers fundamental Java concepts, mainstream frameworks, deep dives into underlying principles, and JVM internals.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.