目录
目录.png
简介
- 每个Thread都持有一个ThrealLocal静态变量,ThreadLocal的实现是这样的:每个Thread 维护一个 ThreadLocalMap 映射表,这个映射表的 key 是 ThreadLocal实例本身,value 是真正需要存储的 Object。
get()方法源码
public T get() {
//1、获取当前线程
Thread t = Thread.currentThread();
//2、 获取当前线程的ThreadLocalMap 对象
ThreadLocalMap map = getMap(t);
if (map != null) {
//3、map不为空,则以当前 ThreadLocal 对象实例作为key值,去map中取值,有找到直接返回
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//4. map 为空或者在map中取不到值,那么走这里,返回默认初始值, 如果涉及null key会清理以防止内存泄露
return setInitialValue();
}
set() 方法源码
public void set(T value) {
//1. 取当前线程对象
Thread t = Thread.currentThread();
//2. 取当前线程的数据存储容器
ThreadLocalMap map = getMap(t);
if (map != null)
//3. 如果map不为空,以当前ThreadLocal实例对象为key,存值
map.set(this, value);
else
//4. 如果map为空,新建一个当前线程的数据存储容器
createMap(t, value);
}
ThreadLocal为什么会内存泄漏
- ThreadLocalMap中的Entry继承WeakReference<ThreadLocal<?>
static class Entry extends WeakReference<ThreadLocal<?>>
- 如果一个ThreadLocal没有外部强引用(项目中一般都会有强引用来引用它所以不会被错误清理, 假如代码直接置为null即取消强引用)来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。
- 其实,ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value。
- 项目中每次结束都会清理设置ThreadLocalMap的value(即调用remove方法),调用value相应的clear方法将其clear防止内存泄漏
ThreadLocalMap中的Entry的key设计成弱引用的原因
- 现在线程基本都是被复用的,为了防止用户忘记调用remove方法而造成不断的内存堆积
ThreadLocalMap中的Entry的value是强引用的原因
- 设置成弱引用的话,可能造成代码里面获取value时为null,因为可能被回收
参考文献
网友评论