ThreadLocal类用来提供线程内部的局部变量
ThreadLocal实例 通常来说都是private static类型的,用于关联线程和线程上下文
ThreadLocal与synchronized的区别
synchronized:同步机制采用'以时间换空间'的 方式, 只提供了一份变量,让不同 的线程排队访问;侧重于多个线程之间访问资源的同步
ThreadLocal采用'以空间换时 间'的方式, 为每一个线程都提供 了一份变量的副本,从而实现同 时访问而相不干扰;侧重于多线程中让每个线程之间的数据 相互隔离。
数据结构
每个Thread
中都具备一个ThreadLocalMap
,而ThreadLocalMap
可以存储以ThreadLocal
为 key ,Object 对象为 value 的键值对。

ThreadLocal与内存泄漏
相关概念
Memory overflow:内存溢出,没有足够的内存提供申请者使用。
Memory leak: 内存泄漏是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪 费,导致程序运行速度减慢甚至系统崩溃等严重后果。内存泄漏的堆积终将导致内存溢出。
强引用("Strong" Reference),就是我们最常见的普通对象引用,只要还有强引用指向一个对象, 就能表明对象还"活着",垃圾回收器就不会回收这种对象。
弱引用(WeakReference),垃圾回收器一旦发现了只具有弱引用的对象,不管当前内存空间足够与 否,都会回收它的内存。
产生原因
java
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
ThreadLocalMap
的 key
和 value
引用机制:
- key 是弱引用 :
ThreadLocalMap
中的 key 是ThreadLocal
的弱引用 (WeakReference<ThreadLocal<?>>
)。 这意味着,如果ThreadLocal
实例不再被任何强引用指向,垃圾回收器会在下次 GC 时回收该实例,导致ThreadLocalMap
中对应的 key 变为null
。 - value 是强引用 :即使
key
被 GC 回收,value
仍然被ThreadLocalMap.Entry
强引用存在,无法被 GC 回收。
当 ThreadLocal
实例失去强引用后,其对应的 value 仍然存在于 ThreadLocalMap
中,因为 Entry
对象强引用了它。如果线程持续存活(例如线程池中的线程),ThreadLocalMap
也会一直存在,导致 key 为 null
的 entry 无法被垃圾回收,即会造成内存泄漏。
也就是说,内存泄漏的发生需要同时满足两个条件:
ThreadLocal
实例不再被强引用;- 线程持续存活,导致
ThreadLocalMap
长期存在。
为什么必须使用弱引用
如果 ThreadLocal 对线程本地变量(ThreadLocalMap)使用强引用,那么当 ThreadLocal 对象不再被外部引用时,由于 ThreadLocalMap 仍然被线程持有强引用,垃圾回收器无法回收 ThreadLocal 对象,从而可能导致内存泄漏。因为线程的生命周期可能很长,大量的无法被回收的 ThreadLocal 对象会占用内存空间
避免措施
remove()