《12个真实项目实战带你玩转Java并发编程》 笔记五 ThreadLocal

一、ThreadLocal解密

 如果让我们自己实现ThreadLocal功能,我们第一反应想到的应该是这么做

  • 新建一个类名为MyThreadLocal
  • 在类种创建一个Map类型的成员变量,key是Thread,value是Object

但这样会有一个很严重的问题,内存泄漏。只要这个MyThreadLocal对象不回收,那Map中的对象也不会被回收,除非每次手动删除,但这样太麻烦了,一不注意就会导致OOM。所以这样设计是有很严重的缺陷的,那JDK中的ThreadLocal是如何是实现的呢?

通过观察ThreadLocal的get方法我们发现,它的ThreadLocalMap不是ThreadLocal中的变量,而是Thread中的一个内部类

 

 再看一下map.getEntry具体是如何实现的

 

 如上图所示,在ThreadLocalMap这个内部类中,有个Entry数组来存储对象,先根据ThreadLocal中的threadLocalHashCode来计算出数组的下标,从而得到Entry对象

二、ThreadLocal会内存泄露吗

我们已知,Thread中有ThreadLocalMap,ThreadLocalMap中由Entry负责存储对象,所以只要Thread能被回收,就不会有内存泄露问题,所以常规的使用是没有问题的

但如果在线程池中使用了ThreadLocal,由于线程池的线程Thread不会被回收,所以此时会有内存泄露问题吗?比如下图这种代码,虽然每个call方法里都创建了ThreadLocal,但最终是分别存储到5个Thread的ThreadLocalMap的Entry数组中的,每个数组有4个对象

我们看存储对象的Entry类,key是ThreadLocal,value是Object,而key是软引用(如下图),即会被垃圾回收

那么模拟一下过程,刚执行完成,还未触发垃圾回收时,ThreadLocalMap如下图所示

 触发垃圾回收后,key被回收,就变成了下图

此时如果再触发get方法,红框2的e是null,所以会进入getEntryAfterMiss方法,在这个方法里会把value设置为null,然后就能value对应的对象给回收了

但只有在调用了set、get、remove方法时才能够触发,如果不再调用,还是会有内存泄露问题。所以可以在不使用的使用调用下remove方法,避免内存泄露

总结一下,是否会发生内存泄漏要看ThreadLocal这个对象是否会被回收。ThreadLocal对象作为ThreadLocalMap中的key,虽然是软引用,但如果有别的强引用在引用ThreadLocal时,即使发生GC也不会被回收。比如ThreadLocal作为类的成员变量,因为类基本不会回收,所以ThreadLocal对象也不会被回收,所以不用考虑内存泄漏问题。如果ThreadLocal是方法的局部变量,那ThreadLocal就可能被回收,就会发生内存泄漏问题

三、InheritableThreadLocal

 ThreadLocal可以实现当前线程间的对象传递,而InheritableThreadLocal则可以让子线程在创建时继承父线程中的对象。如下图代码所示,run方法里可以输出值,但父线程一定要先set才行

 而实现原理则是在new TestInheritableThreadLocal()这步中。因为TestInheritableThreadLocal继承Thread,所以最终会调用Thread的init方法,最关键的即是createInheritedMap方法

 而该方法最终会调用ThreadLocalMap的构造方法。其中parentMap就是父线程的ThreadLocalMap对象,将parentMap中的table复制到了子线程的table中,这样就实现的对象的引用传递

 但由于是创建时进行了拷贝,所以要先在父线程中设置好,才能在子线程中获取对象

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值