在了解ThradLocal原理前,是有必要需要了解一下java的引用类型的,因为Threadlocal的底层使用了弱引用,可以顺便把这方面的知识过一下,再加上面试的时候,这个也是比较常问的
1.java引用类型:
引用类型分别有强,软,弱,虚类型
2.分别说一下这些引用类型的特点
强引用:在java里面直接new出来的对象属于一个强引用,如果一个对象具有强引用,JVM就不会去进行回收它,当内存空间不够的时候就会报OOM错误
软引用:如果一个对象只具备软引用,那么当内存空间不够的时候,JVM就会把这个对象进行回收(Soft Reference)
弱引用:如果一个对象只具备了弱引用,那么被jvm的gc线程检测到了,这个对象就会被回收(Weak Reference)
虚引用:是直接管理本地内存的
3.ThradLocaL
ThreadLocL是线程的一个局部变量,这种变量可以在多线程的情况下,让数据与当前线程进行绑定,这个数据其他线程是不能进行读取跟修改的,从而确保了线程的安全。
4.set方法的底层原理
在ThreadLocL调用set方法的时候,set方法底层是先获取到当前线程,然后再通过getmap方法,来获取当前线程的ThreadLocLs变量,然后再把ThradLocL作为key,set方法传进参数作为value,通过ThreadLocLs的set方法保存起来,在保存的时候,key会以弱引用的形式保存起来,value是以强引用的形式进行保存
所以当Threadlocl调用set方法来保存值的时候,并不是保存的在Threadlocl里面,而是保存在了当前线程的ThreadLocLs变量中(这个变量的类型是ThreadLocalMap),而这个变量是一个map结构,里面的key保存了调用set方法的ThreadLocL引用,value是需要保存的值
5.get方法的底层原理
ThreadLocL调用get方法,首先会获取当前线程,然后再通过getmap方法,来获取当前线程的ThreadLocLs变量,通过变量的getEntry方法,参数是ThreadLocL,就可以来获取到所保存的数据
6.为什么key要设置为弱引用,强引用不行吗?
用强引用的话有可能会造成内存泄露,当线程不再需要使用某个Threadlocl的时候,因为key是弱引用,所以只要被jvm的gc线程检测到就会被回收,如果key是强引用类型,那么这个key就没有办法被回收,如果有很多线程的Threadlocl一直没有被使用,然后又没有被回收,那么时间长了就有可能爆oom错误
7.为什么value要设置为强引用,弱引用不行吗?
如果value是弱引用,只要jvm执行一次gc操作,那么这个value就被回收掉了,取值的时候会返回null,所以只能是强引用
8.内存泄露
第一种情况:如果key被jvm的gc回收掉了,那么value值就无法被访问,如果线程一直在运行,那么就会有可能造成oom错误,虽然调ThreadLocL的set,get方法的时候会把key为null的Entry对象删除掉,但如果这个线程很久都没有进行这两个方法的调用,那也是无法进行回收,所以我们在使用完成之后要手动remove来保证代码的安全性
第二种情况:如果使用了线程池进行线程复用,线程使用完成之后,如果不进行remove操作的话,那么线程的ThreadloclMap保存的值会越来越多,还可能出现value的值被覆盖的情况,所以也需要手动remove操作来保证代码的安全性
9.ThreadLocalMap.Entry中的key为什么不会被错误清理?
一个对象在只有弱引用指向它时,一旦jvm执行gc操作,那它就会被回收,但是ThreadLocal对象除了在Entry中有弱引用指向,在业务代码中还有强引用指向,所以不会被错误清理
10.ThreadLocal在业务场景中的使用
全局存储用户信息,在我负责的项目里面,是有过滤器的,在过滤器中对token进行验证,验证成功之后,就把对应的token以及该用户比较常用的数据保存到ThreadLocal,然后需要就可以直接使用,但是在使用之后,需要及时把ThreadLocl里面的数据给清理,否则容易内存泄漏