栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

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

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

《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方法,避免内存泄露

总结一下,由于ThreadLocalMap包含了ThreadLocal,且线程Thread中包含变量ThreadLocalMap,因此ThreadLocalMap与Thread的生命周期是相同的,所以在线程池场景中使用ThreadLocal的时候,我们还是要养成好习惯,ThreadLocal不在使用的时候调用remove方法,避免内存泄漏情况发生

三、InheritableThreadLocal

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

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

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

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/678610.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号