ThreadLocal
1.概念
《Java并发编程的艺术》一书中:TreadLocal,即线程变量,是一个以TreadLocal对象为键,任意对象为值的存储结构。这个结构被附带在线程上,也就是说一个线程可以根据一个ThreadLocal对象查询到绑定在这个线程上的一个值。
2.ThreadLocal的使用
ThreadLocal主要通过get和set函数进行使用,源码如下:
2.1 set函数
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
可以看到ThreadLocal先获得当前线程的中的ThreadLocalMap,如果map已经初始化,就在map中加入value,如果没有初始化,就调用creatMap进行初始化。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
2.2 get函数
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
如果是数据为null,则初始化,初始化的结果,TheralLocalMap中存放key值为threadLocal,值为null。
3. ThreadLocal的内存结构
每个线程都有一个私有的ThreadLocalMap表,其中key为ThreadLocal对象,value为泛型T。
4. 内存泄露问题
ThreadLocalMap中的key是弱引用(防止该回收ThradLocal的时候,因为线程持有ThradLocal对象的强引用而导致无法被回收),当外部不存在强引用的时候,就会被自动回收,但value是强引用,所以会造成key为null的value。而线程池中的线程基本上不会主动结束,所以value基本不会被清理。
key如果使用强引用:多个线程依赖同一个 ThreadLocal ,此时 线程 1 的 ThreadLocal 使用结束了想要释放内存,但是由于是强引用(因为还有其他线程还在指向 ThreadLocal),这就导致了线程1 的持有 ThreadLocal的 Entry 占有的内存无法释放,导致了内存泄露,使用弱引用的时候,这种问题就可以解决。
解决办法是:
ThradLocal自己的解决方法:在ThreadLocalMap进行set(),get(),remove()的时候,都会进行清理泄漏的Entry
我们可以使用的的解决方法:1.每次使用完ThreadLocal,主动调用remove()。2.把ThreadLocal声明为全局变量,使他无法被GC回收
参考来源
《并发编程的艺术》
https://blog.csdn.net/u010445301/article/details/111322569
https://blog.csdn.net/weixin_44184990/article/details/122279854
https://zhuanlan.zhihu.com/p/369953316