ThreadLocal和引用关系

一、引用类型-强软弱虚

强引用:通常=表示的引用
软引用:SoftReference,在内存不足时,垃圾回收器才回收
弱引用:WeakReference,无论内存是否足够,只要 JVM 开始进行垃圾回收,那些被弱引用关联的对象都会被回收。ThreadLocal中使用了弱引用
虚引用:最弱的一种引用,垃圾回收器直接回收,主要堆外内存管理场景。例如:通过虚引用关联一块堆外内存的对象,当虚引用对象被回收后,引用堆外内存还没有回收,因此需要通知(采用一个Queue方式)gc进行回收

二、ThreadLocal

2.1、内部实现

ThreadLocal线程本地变量,主要用于存储线程自己特有数据。注:每个Thread对象内部都有一个ThreadLocalMap成员变量threadLocals,但new Thread对象之后该变量任然是null,只有在使用ThreadLocal对象进行存储的时候才会进行分配。ThreadLocal中set方法代码如下:

 public void set(T value) {
     Thread t = Thread.currentThread();
     ThreadLocalMap map = getMap(t);//获取Thread对象中threadlocals成员变量
     if (map != null)
         map.set(this, value); //设置线程本地变量
     else
         createMap(t, value); //对Thread对象中成员变量threadlocals初始化
 }

通过以上代码可知:
1)一个Thread对象可以存储多个ThreadLocal对象
2)通过map.set可知,每个threadlocal只能存一个value数据。(map解决hash冲突,采用的开发地址法
3)ThreadLocal是线程安全的

2.2、ThreadLocal什么地方用的弱引用?为什么要用弱引用?

Thread对象有一个ThreadLocalMap类型的成员变量,该map内部定义了Entry,该Entry对象继承了WeakReference。不使用弱引用可能会存在内存泄露。

1)首先堆内存中创建了一个ThreadLocal对象m,通过局部变量tl进行强引用。

2)当我们通过tl.set方法添加数据,会将对象m作为Entry-key,以及业务数据作为Entry-value,添加到map当中。

3)map和线程对象是强引用关系。

4)假设将弱引用改成强引用。当tl局部变量设置为null或者函数结束,理论上说tl和对象m之前引用关系结束了,GC线程是可以回收对象m,实际呢?由于线程未结束,那么强引用关系就一直存在,GC就无法回收。所以这个地方改成弱引用。

当然,线程结束了,线程对象会被回收,对象m也会被回收(但是往往线程是常驻内存的)。

2.3、另外一个内存泄露--Entry对象value

由于弱引用关系,GC会将对象m进行回收,那么Entry-key这个地方就会设置成null,但是Entry-value仍然存在于map当中,因此也会出现内存泄露。所以当不使用ThreadLocal对象时,不能直接设置为null或者函数直接返回,必须手动调用remove()方法,否则出现内存泄露

猜你喜欢

转载自blog.csdn.net/xxb249/article/details/115518236