浅析ThreadLocal的底层实现线程隔离+内存泄漏

Posted 噫!微斯人,吾谁与归

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅析ThreadLocal的底层实现线程隔离+内存泄漏相关的知识,希望对你有一定的参考价值。

首先ThreadLocal有什么作用?

它最为突出的特点就是"线程隔离",可能你心中会疑问线程隔离是什么?此刻可以拿synchronized做对比,在并发环境下,synchronized将共享资源进行锁定,线程必须一个个的去挨个访问。而ThreadLocal它相当于每个线程都有一份该资源,然后线程之间对资源的操作是互不可见的;

可以这样说:synchronized采用的是以时间换空间的方式,而ThreadLocal采用的是以空间换时间的方式。

ThreadLocal如何使用呢?

第一个线程执行local.set(“1111”),然后在另外一个线程中先获取local中的值;最后在自己的线程内部设置值并获取

从结果可以看出,它的确做到线程隔离了。

好奇不,它怎么做到的呢?


查看getMap方法的具体实现

📑首先调用了getMap()方法,这个方法是获取线程Thread类中的一个ThreadLocalMap属性,这个属性是一个key-value形式;它将ThreadLocal作为key,将隔离的数据作为value存入ThreadLocalMap集合中。

✨简单一句话就是:将ThreadLocal和隔离数据作为key-value,存入了Thread中的ThreadLocalMap集合中,所以只能本线程访问,因此做到了线程隔离。

❓ThreadLocalMap的内部实现是怎么样的?

ThreadLocalMap是ThreadLocal的一个静态内部类,你会发现它内部的Entry居然是一个弱引用的一个子类(Entry不用多说,了解过Map的都明白)


❓为什么Entry节点要继承弱引用,而不是强引用呢?


当我们不再使用这个ThreadLocal时,直接将t=null即可;此时ThreadLocal只有一个弱引用与之相连,而弱引用指向的对象在GC时就会被回收;所以使用WakeRefenence一定程度上避免了内存泄漏。


ThreadLocal为空,则key=null;此时有一个大问题,key一旦为null,那么value将无法获取;除非线程结束,否者它将一直存在,这时一个隐含的内存泄漏问题。官方对其也进行了优化,在ThreadLocal的get/set方法中,会清除所有key=null的value。

虽然官方已经做出了优化,但是使用线程池还是会内存泄漏的隐患,线程使用完之后直接放回到了线程池,此时线程存活->ThreadLocalMap存活->数据存活;这就导致了内存泄漏,所以最后的办法就是使用完ThreadLocal及时remove掉数据。

❓如果避免上面的内存泄漏?

📑在使用完ThreadLocal时,及时调用它的的remove方法清除数据。

❓ThreadLocal的使用场景?

📑如果希望将类的某个静态变量与线程状态关联,可以考虑使用ThreadLocal。ThreadLocal的设计本身就是为了能够在当前线程中有属于自己的变量,并不是为了解决并发或者共享变量的问题。

以上是关于浅析ThreadLocal的底层实现线程隔离+内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

浅析ThreadLocal的底层实现线程隔离+内存泄漏

浅析 ThreadLocal

详解ThreadLocal原理及内存泄漏

浅析 Java ThreadLocal

ThreadLocal浅析

源码浅析—ThreadLocal