一、先说结论
ThreadLocal 解决的不是'共享变量怎么同步',而是'每个线程怎么拿到自己的那份数据'。它把值放进当前线程内部的 ThreadLocalMap,读写都只在当前线程里完成,所以天然隔离。
这套设计很适合放上下文信息、线程内复用的对象,代价也很明确:如果在线程池里忘了清理,值会跟着线程活很久,最后变成一笔不太好查的内存账。
二、它到底怎么存的
// 每个 Thread 对象内部都有一个 ThreadLocalMap
ThreadLocal.ThreadLocalMap threadLocals = null;
// ThreadLocalMap 内部使用 Entry 数组,Entry 继承自 WeakReference<ThreadLocal<?>>
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k); // 弱引用指向 ThreadLocal 实例
value = v; // 强引用指向实际存储的值
}
}
关键点就两个:
- 每个线程都有自己的
ThreadLocalMap Entry的 key 是弱引用,value 是强引用
所以同一个 ThreadLocal 实例,在不同线程里会落到各自线程的 map 里,彼此看不见。它不像 ConcurrentHashMap 那样解决并发共享问题,ThreadLocal 干脆把共享这件事绕开了。
三、set() 和 get() 的实际流程
set() 的逻辑不复杂:先拿当前线程,再找当前线程的 map;有就插入,没有就创建。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value); // this 指当前 ThreadLocal 实例
} else {
createMap(t, value);
}
}
private {
Entry[] tab = table;
tab.length;
key.threadLocalHashCode & (len - );
( tab[i]; e != ; e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
(k == key) {
e.value = value;
;
}
(k == ) {
replaceStaleEntry(key, value, i);
;
}
}
tab[i] = (key, value);
++size;
(!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}


