直接回答:ThreadLocal 通过"每个线程各自持有一份变量副本"实现线程隔离。每个 Thread 对象内部有一个 ThreadLocalMap,其 Entry 以 ThreadLocal 对象的弱引用为 key、以真正的值(强引用)为 value;get()/set() 先取当前线程的 Map 再操作,因此互不干扰。
展开解析:内存泄漏的根源在引用链:线程 → ThreadLocalMap → Entry → value 是强引用。当外部不再持有某个 ThreadLocal 时,弱引用的 key 会被 GC 回收变成 null,但 value 仍被这条强引用链拽住;而线程池中的线程长期存活,这些"key 为 null 的 Entry"就永远无法回收,越积越多。注意弱引用只是缓解手段,不是泄漏的根因——根因是线程生命周期远长于 ThreadLocal。
正确用法:用完在 finally 中调用 remove(),尤其是线程池场景:
try {
contextHolder.set(ctx);
...
} finally {
contextHolder.remove();
}
补充:ThreadLocalMap 的 set/get 会顺带清理部分 key 为 null 的过期 Entry(启发式清理),但不能依赖。追问:InheritableThreadLocal 与线程池不兼容的问题、TransmittableThreadLocal 的解决思路。