Java中的线程ThreadLocal详细解析
作者:buzhbuzh
一 Java中的引用关系
Java中的引用分为了强引用、弱引用、软引用、虚引用
- 强引用: 垃圾回收的时候,如果内存不足也不会进行垃圾回收,会报out-of-memory异常
- 弱引用: 垃圾回收的时候,如果内存不够,则数据被回收.
- 软引用: 垃圾回收的时候,软引用会立刻进行回收
- 虚引用: 垃圾回收的时候,软引用会立刻进行回收
二 ThreadLocal代码实现
ThreadLocal是线程本地变量,存储在ThreadLocal里面的数据都是 线程安全的.
一般ThreadLocal适用的场景多是各个线程间没有变量共享需要的同步问题场景,比如一个简单的SimpleDateFormat类,该类不是线程安全的,却是有状态的,如果将SimpleDateFormat设置为静态的,所有线程共享,那么就会出现线程安全的问题,其中一个线程修改日期格式为“yyyyMMdd”,然后可能会影响到另外一个需要格式为“yyyy-MM-dd”的线程。但是如果在方法中每次用到的时候都new一个SimpleDateFormat类又太费内存(SimpleDateFormat这种简单的类倒还好,若是一些比较占用资源的比如数据库连接类等,就会让加大整个数据库的压力),这时候采用ThreadLocal为每一个线程保存一份SimpleDateFormat副本,这样既不会说每次调用方法都会生成一个对象,也不会在并发时产生线程同步问题。这就像是在共享一个静态变量和每次使用都new一个对象两者之间的一种折中处理方法。
自己实现的ThreadLocal
通过一个全局map, key是Thread对象,value是对应存储的数据,这种情况下 如果map不销毁,对应的Thread线程就不发被回收,导致内存泄露
JDK中使用的ThreadLocal
JDK8中在Thread对象中引用ThreadLocalMap,ThreadLocalMap的生命周期是Thread是同一个生命周期.
ThreadLocalMap的实现
(1) Entry数组实现
Entry数组是线程中实际存放数据的地方,key是ThreadLocal,是一个弱引用,value是对应存储的值.
为什么ThreadLocalMap#key是一个弱引用
如果外部不使用了ThreadLocal,则对应的数据不应该存储在ThreadLocal中,应该被删除掉,防止内存泄漏
ThreadLocalMap的属性
private static final int INITIAL_CAPACITY = 16; //存储数据的地方 private Entry[] table; // //数量 private int size = 0; private int threshold; // Default to 0
ThreadLocal的方法
ThreadLocal#set()方法
(1) 第一步 获取到当前线程的ThreadLocalMap
(2) 第二步 初始化ThreadLocalMap
(3) 第三步 将ThreadLocal作为key,value值作为值,设置到map中
ThreadLocal # get()方法
方法主要完成两个功能: (1) 完成数据的读取 (2) 将ThreadLocalMap中key为NULL的数据从ThreadLocalMap中删除
ThreadLocalMap中的entry其实是继承的弱引用,如果该弱引用指向的ThreadLocal没有在外部被强引用指向的话,在下次gc的时候就会被回收,那这样的话就会出现ThreadLocalMap中存在key为null的情况,这样的数据对于map来讲是脏数据,这样的脏数据没有用,却一直占用着map的存储空间,这其实就是一种内存泄漏,所以需要来释放掉这些空间
三 ThreadLocalMap的内存泄漏问题
如果ThreadLocal无强引用进行应用,则在垃圾回收的时候会把这个key进行回收.
这个时候,因为线程还存活,所以ThreadLocalMap依旧存在,但是key为null,这个 key对应的value就无法被访问到了,造成内存泄漏.
JDK团队自己的解决办法
调用get()、set()方法的时候,自动清理掉ThreadLocalMap中key为null的数据
开发者的解决办法 使用ThreadLocal后,调用ThreadLocal
到此这篇关于Java中的线程ThreadLocal详细解析的文章就介绍到这了,更多相关Java的ThreadLocal内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!