一、定义
https://baijiahao.baidu.com/s?id=1653790035315010634&wfr=spider&for=pc
threadlocal而是一个线程内部的存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以得到存储数据
当使用ThreadLocal维护变量的时候 为每一个使用该变量的线程提供一个独立的变量副本,即每个线程内部都会有一个该变量,这样同时多个线程访问该变量并不会彼此相互影响,因此他们使用的都是自己从内存中拷贝过来的变量的副本, 这样就不存在线程安全问题,也不会影响程序的执行性能。
但是要注意,虽然ThreadLocal能够解决上面说的问题,但是由于在每个线程中都创建了副本,所以要考虑它对资源的消耗,比如内存的占用会比不使用ThreadLocal要大。
举例:
ThreadLocal<String> localName = new ThreadLocal(); localName.set("小狼"); String name = localName.get();
在线程1中初始化了一个ThreadLocal对象localName,并通过set方法,保存了一个值小狼
,同时在线程1中通过localName.get()
可以拿到之前设置的值,但是如果在线程2中,拿到的将是一个null。
二、ThreadLocal的工作原理
1.每个Thread实例内部都有一个ThreadLocalMap,ThreadLocalMap是一种Map,它的key是ThreadLocal,value是Object。
2. 存数据 是往当前线程的ThreadLocalMap中存入数据,其key是当前ThreadLocal对象,value是set方法中传入的值。
3.使用数据时,以当前ThreadLocal为key,从当前线程的ThreadLocalMap中取出数据。
三、ThreadLocal内存泄漏分析与解决方案
ThreadLocalMap 中使用的 key 为 ThreadLocal 的弱引用,而 value 是强引用。所以,如果 ThreadLocal 没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。这样一来,ThreadLocalMap 中就会出现key为null的Entry。假如我们不做任何措施的话,value 永远无法被GC 回收,这个时候就可能会产生内存泄露。ThreadLocalMap实现中已经考虑了这种情况,在调用 set()、get()、remove() 方法的时候,会清理掉 key 为 null 的记录。使用完 ThreadLocal方法后 最好手动调用remove()方法
造成内存泄漏的原因:
1.key为弱引用,gc之后key为null,导致value无法被获取到
2.ThreadLocalMap的生命周期与Thread相同,导致内存泄漏一直存在
解决方案:
每次使用完ThreadLocal,都调用它的remove()方法,清除数据。
通过下图进行分析:
ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value
现在,我们假设ThreadLocal完成了自己的使命,与ThreadLocalRef断开了引用关系。此时内存图变成了这样。
系统GC发生时,由于Heap中的ThreadLocal只有来自key的弱引用,因此ThreadLocal内存会被回收到。
到这里,value被留在了Heap中,而我们没办法通过引用访问它。value这块内存将会持续到线程结束。造成内存泄漏。