结构
ThreadLocal
类提供了一个简单的接口,主要有以下几个方法:
void set(T value)
: 设置当前线程的局部变量的值。T get()
: 返回当前线程所对应的局部变量的值。T initialValue()
: 如果线程还没有局部变量的副本,则像下面这样提供一个初始化值。void remove()
: 清除当前线程的局部变量副本。
源码结构
public class ThreadLocal<T>{
...
static class ThreadLocalMap{
private Entry[] table;
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
}
ThreadLocal中用于保存线程的独有变量的数据结构是一个内部类:ThreadLocalMap
,是一个K/V结构
。
key
:当前ThreadLocal的对象引用
value
:要保存的目标值
内存泄漏的原因
ThreadLocal对象在栈的引用结束后,ThreadLocalMap中的key还是有引用指向了ThreadLocal对象
ThreadLocalMap是当前线程的一个成员变量,它存在一条引用指向Entry对象的value对象,如果当前线程处于线程池的复用状态,那么线程就会一直存活,那么该引用就一直存在。
解决方案
针对第1点,JDK源码中key的结构是:
WeakReference<ThreadLocal<?>>,通过弱引用下次GC时进行回收释放
针对第2点,在使用完ThreadLocal之后,最好在finally模块进行调用ThreadLocal.remove()方法,下次GC时清理Entry对象
ThreadLocal和ThreadLocalMap
ThreadLocal
类本身并不存储线程局部变量的值,它只是用来访问线程局部变量的一个工具。每个线程(
Thread
类的实例)内部维护了一个ThreadLocalMap
。这个映射是线程私有的,用来存储ThreadLocal
变量的副本。ThreadLocalMap
的键是ThreadLocal
对象的弱引用(weak reference),而值是线程局部变量的实际值。
ThreadLocalMap结构
ThreadLocalMap
是ThreadLocal
类的一个静态内部类。每个
Thread
对象都有一个ThreadLocalMap
实例作为其线程局部变量的存储。当
ThreadLocal
的set()
方法被调用时,它会访问当前线程的ThreadLocalMap
,并将ThreadLocal
对象自身作为键(通过ThreadLocalMap
的ThreadLocalHash
实现),将值作为ThreadLocalMap
的值存储起来。
为什么值不会互相影响
尽管
threadLocalValue
这个引用在多个线程之间是共享的,但是每个线程通过threadLocalValue
访问的ThreadLocalMap
是独立的。每个线程的
ThreadLocalMap
中ThreadLocal
对象(作为键)是相同的,但是由于ThreadLocalMap
是线程局部的,因此每个线程实际上访问的是自己的ThreadLocalMap
实例。
总结
ThreadLocal
的设计确保了每个线程可以访问自己的局部变量副本,即使 ThreadLocal
对象本身是共享的。这是通过在每个线程的 Thread
实例中维护一个独立的 ThreadLocalMap
来实现的。这种设计允许 ThreadLocal
变量在多线程环境中安全地使用,而不必担心数据的并发访问问题。
评论区