目录弱引用ThreadLocalThreadLocal与线程的关系ThreadLocalMap中的Entry
弱引用
什么是虚引用?简单的来说,下一次垃圾回收时,无论内存是否充足,都会被垃圾回收。那这玩意有什么用呢?看下面一段代码,相信对弱引用会有更好的理解:
WeakReference
System.out.println(Objects.isNull(weakReference.get())); //输出false
weakReference = null;
System.gc();
System.out.println(Objects.isNull(weakReference.get())); //输出 true
ThreadLocal
threadLocal又是什么呢?随便百度,就能够得到大概的答案,threadLocal主要用于解决线程并发问题,将共享变量拷贝到一份到自己的线程,来避免并发修改以及竞态问题。那这其中有哪些坑呢?
ThreadLocal与线程的关系
看了ThreadLocal的基本API,可能会想当然的以为拷贝的数据就是存在ThreadLocal,那可就大错特错啦。事实上ThreadLocal只是起到一个桥梁的作用。真正的数据是和线程绑定的,存在Thread的ThreadLocalMap中。
ThreadLocal
threadLocal.set(new Object());
// 业务
threadLocal.get();
在此具体的细节不在一一分析。
其实Thread内部内置了一个Map,Key为ThreadLocal,value为set的值,Entry本身为WeakReference的子类。
static class Entry extends WeakReference
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = v;
}
}
线程中保存了一个ThreadLcoalMap 通过具体的ThreadLocal对象作为key,从ThreadLoacalMap中拿到Value
ThreadLocalMap中的Entry
问题又来了,为什么要用Entry要使用弱引用呢,其实大致大家都有一个答案?防止内存泄漏。那为什么弱引用可以防止泄露呢?一定可以避免吗?
我们看下下一段代码:
while (true){
ThreadLocal
threadLocal.set(new byte[1024*1024*10]);
}
这段代码,跑一年也没啥事,原因就是弱引用的存在。因为每一次循环threadLocal的生命周期都会结束,根据可达性分析算法,当触发GC时,在过去循环产生的ThreadLcoal都将会被垃圾回收,有人可能会说了,ThreadLocalMap中不是还存在引用吗,巧了,那个是弱引用,不算数。此时Entry的Key也就是变成NULL,注意:
此时Entry还是存在的,只是你调用Entry#get()方法将会变成NULL。
每次调用set和get方法的时候,如果遇到key为NULL的Entry都会清理掉,这才能保证Entry被完整的回收。
那再看下面一段代码:
ThreadLocal
while (true){
ThreadLocal
threadLocal.set(new byte[1024*1024*10]);
ref[(int)i] = threadLocal;
++i;
}
在数组还没越界前,没多久就会OOM,因为threadlocal的强引用还在,会导致无法被回收。也就是说弱引用也并不是万能的,还是建议搭配remove方法一起食用
综上,就是笔者对于ThreadLocal的一些理解,若有不足,请多多指教