threadlocal 的 set 方法将值存入当前线程的 threadlocalmap 中,以当前 threadlocal 实例为 key、用户 value 为 value;每个线程独有 threadlocals 字段,map 使用弱引用 key 防泄漏,但需显式 remove() 避免 value 内存泄漏。

ThreadLocal 的 set 方法并不是把值直接存到 ThreadLocal 实例本身,而是把值存到**当前线程的 Thread 对象内部的一个 Map(即 ThreadLocalMap)中**,以当前 ThreadLocal 实例为 key,用户传入的 value 为 value。
核心机制:每个线程独有 ThreadLocalMap
每个 Thread 对象都持有一个名为 threadLocals 的私有字段,类型是 ThreadLocal.ThreadLocalMap。这个 Map 不是 Java 原生的 HashMap,而是 ThreadLocal 自定义的轻量级哈希表,专为单线程访问优化,不支持多线程并发操作(所以无需锁)。
调用 threadLocal.set(value) 时,实际执行流程如下:
- 获取当前线程:
Thread t = Thread.currentThread(); - 从该线程中取出它的
ThreadLocalMap:ThreadLocalMap map = t.threadLocals; - 如果 map 为空,先初始化一个:
createMap(t, value); - 否则,调用
map.set(this, value),其中this是当前 ThreadLocal 实例
ThreadLocalMap 的 set 操作细节
ThreadLocalMap.set() 使用“线性探测法”解决哈希冲突,key 是 ThreadLocal 引用,value 是用户数据。关键点包括:
- hash 计算基于 ThreadLocal 的
threadLocalHashCode(一个每次新建实例就递增的伪随机数,非 Object.hashCode) - Entry 是弱引用键(
WeakReference<threadlocal>></threadlocal>),防止 ThreadLocal 泄漏——当 ThreadLocal 被回收后,Entry 的 key 变为 null,后续触发清理逻辑可回收 value - 插入时若发现 key 为 null 的 stale entry(陈旧条目),会尝试覆盖并触发一次小范围探测清理
为什么 set 后其他线程拿不到?
因为每个线程操作的是**自己 Thread 对象里的 threadLocals Map**,彼此完全隔离。即使多个线程使用同一个 ThreadLocal 实例调用 set,它们写入的是各自独立的 Map,互不影响。
例如:
Thread A 执行 tl.set("A") → 写入 A.threadLocals["tl"] = "A"Thread B 执行 tl.set("B") → 写入 B.threadLocals["tl"] = "B"
两者无任何交集
注意内存泄漏风险
虽然 key 是弱引用,但 value 是强引用。如果线程长期运行(如线程池中的线程),而 ThreadLocal 实例已不再使用但未调用 remove(),那么 value 仍被 Map 中的 Entry 强引用,无法回收,造成内存泄漏。
- 最佳实践:在线程复用场景(如 Web 容器、线程池)中,务必在使用完后显式调用
tl.remove() - 不要依赖“key 被回收后自动清理 value”——清理只在 set/get/remove 等操作中被动触发,不保证及时
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











