ThreadLocal通过为每个线程维护独立的ThreadLocalMap实现线程隔离,其中key为弱引用的ThreadLocal实例、value为强引用的变量副本;因value强引用且线程长期存活易致内存泄漏,须显式调用remove()避免。

ThreadLocal 通过为每个线程单独维护一份变量副本,实现线程间的隔离。它不靠锁,也不共享内存,核心在于“每个线程独有一份”。
ThreadLocal 的底层结构:Thread → ThreadLocalMap → Entry
每个 Thread 对象内部持有一个 ThreadLocalMap(弱引用的哈希表),这个 map 的 key 是 ThreadLocal 实例本身,value 是该线程专属的变量值。
关键点:
- ThreadLocal 本身只是一个“访问入口”,真正存储数据的是每个线程自己的 ThreadLocalMap
- Entry 继承自 WeakReference<ThreadLocal>,key 是弱引用,防止 ThreadLocal 被长期持有导致内存泄漏
- 不同线程调用同一个 ThreadLocal 的 get()/set(),操作的是各自 Thread 对象里的 map,互不影响
set() 和 get() 是怎么做到隔离的?
调用流程完全依赖当前线程上下文:
立即学习“Java免费学习笔记(深入)”;
- set():获取当前线程(Thread.currentThread())→ 拿到它的 threadLocals(即 ThreadLocalMap)→ 以当前 ThreadLocal 为 key,存入 value
- get():同样先取当前线程 → 取它的 threadLocals → 用当前 ThreadLocal 当 key 查找对应 value;若 map 为空或没找到,会触发 initialValue() 初始化
没有跨线程数据传递,也没有同步逻辑 —— 隔离天然成立。
为什么可能内存泄漏?如何避免?
因为 Entry 的 key 是弱引用,而 value 是强引用。当 ThreadLocal 实例被回收后,key 变 null,但 value 仍被 map 持有,若线程长期运行(如线程池中的线程),就可能堆积无法回收的对象。
规避方法:
- 使用完后显式调用 remove(),清空当前线程 map 中对应的 Entry
- 避免在线程池中使用匿名内部类或 Lambda 定义 ThreadLocal(容易延长 ThreadLocal 实例生命周期)
- 尽量让 ThreadLocal 是 static 的,生命周期可控,且复用安全
不是“线程安全”的替代品
ThreadLocal 解决的是“线程间数据隔离”,不是“多线程并发修改同一对象”的问题。比如:
- 用 ThreadLocal 存 SimpleDateFormat 是对的(避免非线程安全的 DateFormat 并发问题)
- 但若多个线程通过 ThreadLocal 各自拿到一个 List,又都往自己那份里 add 元素,这不解决共享资源竞争,也不影响其他线程 —— 这正是隔离的目的
它不保证变量本身的线程安全,只保证每个线程看到的是自己的副本。


















