一、底层原理
1.数据结构
- 每个
Thread
内部维护一个ThreadLocalMap
(类似 HashMap)。 ThreadLocalMap
的 Key 是 ThreadLocal 实例(弱引用),Value 是存储的值(强引用)。
2.核心操作
ThreadLocalMap类结构:
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;
}
}
private Entry[] table;
// ......
}
class Thread implements Runnable {
// ......
ThreadLocal.ThreadLocalMap threadLocals = null;
// ......
}
ThreadLocalMap内部是Entry数组,Entry的k为当前线程且为弱引用,同时为线程类Thread的内部属性。
set()方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = t.threadLocals;
if (map != null) {
map.set(this, value); // this 指当前ThreadLocal对象
} else {
t.threadLocals = new ThreadLocalMap(this, value);
}
}
set时先获取当前线程的ThreadLocalMap,用当前线程作为key进行赋值。
如果当前线程的threadLocals为空,就会new ThreadLocalMap(this, value) 绑定当前线程。
get()方法:
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();
}
二、相关问题
1.内存泄漏
- 原因:
1:ThreadLocalMap 的 Key(ThreadLocal)是弱引用,会被 GC 回收,导致 key=null
的 Entry。
2:Value 是强引用,线程未结束则 Value 无法回收。
- 解决方案:
每次使用完必须调用 remove() 清理 Entry。
try {
threadLocal.set(data);
// ... 业务逻辑
} finally {
threadLocal.remove(); // 强制清除
}
2.脏数据
- 原因:
线程池复用线程时,未清理 ThreadLocal 导致残留旧数据。
- 解决方案:
同上,用 try-finally
确保 remove()
。
3.弱引用 Key 的清理
- 优化机制:
ThreadLocalMap 在 set()/get()
时会清理 key=null
的 Entry,但依赖调用时机。
- 主动防护:
不要依赖自动清理,显式调用 remove()
。
4.子线程继承问题
默认行为:子线程无法获取父线程的 ThreadLocal 值。
解决方案 :使用 InheritableThreadLocal
(创建子线程时复制父线程数据)。