ThreadLocal源码学习分析
1.ThreadLocal简介
ThreadLocal,顾名思义:线程本地变量。相当于在每个线程下都创建该变量的副本,每个线程之间互不影响。
2.大致的实现思路
在每个Thread类下面,都有一个ThreadLocalMap,这Map用来存储Key为ThreadLocal的键值对,每次get()或者 set(),首先获取线程,然后通过线程获取的map,由于每个线程都不一样,获取的map也就不同,在通过TheadLocal作为Key,进行相应的get和set操作。这样就能保证线程安全性了。
3.ThreadLocal成员变量
ThreadLocal成员变量这里用到AtomicInteger变量,保证了多线程的线程安全。
4.ThreadLocal的核心:ThreadLocalMap
ThreadLocalMap通过上图我们可以发现,ThreadLocalMap是一种类似于Map的实现,Entry是继承WeakReference,是弱引用,Entry的Key是以弱引用的方式进行链接的。
这里如果采用强引用,那么ThreadLocal变量的生命周期就和线程一致了,如果我们在使用线程池的时候,线程的生命周期很长,不对ThreadLocalMap中删除ThreadLocal对应的键值对,就会造成ThreadLocal和Value一直存在内存中。采用弱引用的方式,可以在下次访问Map的时候,遍历Map,删除那些Key为null的键值对。
5.从get()方法分析一下ThreadLocal
首先是获取当前的线程,然后用线程去获取ThreadLocalMap,然后用this,也就是ThreadLocal变量去获取Entry。
计算位置,这里计算位置和HashMap中计算位置是一样的,都是通过和length-1进行与运算。在HashMap源码分析中已经解释了。
如果恰好找到了对应的值,就直接返回,否则进行线性探测
如果Entry不为空,且key相等,则放回,如果Entry不为空,Key不相等,说明这个Entry已经失效,则将这个Entry进行删除,如果没找到就返回空
上面的代码是将失效的Entry进行删除,删除后,将size–,然后对后面Entry进行rehash,并且清除无效的Entry,直到Entry为空,这个操作可以防止存在冲突的多个Entry之间不会出现null的Entry。
6.ThreadLocal内存泄漏问题
理论上来说,TreadLocal是不存在内存泄漏的问题,因为采用的是弱引用的方式,就算ThreadLocal对应的强引用断开了,ThreadLocalMap有可能会在get()或者set()对已经失效的Entry进行删除。
但是,如果你使用线程池的时候,在ThreadLocal中存入了对象,之后就再也不用ThreadLocal了,这时候ThreadLocalMap是不会自动清除失效的Entry的,只有使用get和set的时候才有可能进行删除。
所以,使用ThreadLocal变量的时候,记得用完后调用remove()进行删除。