1.基本介绍
(1)每一个Thread里面有一个ThreadLocalMap对象,里面可以存储ThreadLocal(hashcode是重写的,使用的是黄金分割数,目的是为了让他在ThreadLocalMap中分布的更均匀)为key,set的数据为value放入ThreadLocalMap中
(2)ThreadLocal本身体是强引用,但是作为ThreadLocalMap的Entry的key时候,会继承弱引用,当ThreadLocal的实例数据被强引用释放时,Entry还会弱引用这个实例对象,不过随时会被GC回收掉
(3)当key被GC之后就变成了null,若不及时的remove 就会造成value永久存在从而造成数据泄露,另外在线程池中的线程也遵从1,2若不及时存储,就有可能造成脏数据,简简单单的存放获取新数据没什么问题,假如直接获取数据的话就会出现安全问题
2.ThreadLocal set方法
set方法被调用时候,会获取set运行的线程中的ThreadLocalMap对象
1.如果不是空的话就将value设置到Map
2.如果是空
就创建一个ThreadLocalMap对象,创建容量为16的Entry数组,设置length*2/3的阈值,size+1,并赋值给Thread的threadLocals引用上,并将要set的value 放入map中
Thread中的ThreadLocalMap是懒初始化,
A,ThreadLocalMap的set方法过程如下
1.&到的下标所对应的Entry为空,
直接包装value到Entry中插入到该下标
2.该下标的Entry不是空,
Entry的key跟要set的key相同,直接更新当前Entry的value
3.下标的Entry不是空,但Entry的key是null(假定当前位置是staleSlot)
A。staleSlot所对应的Entry的key为null
B,从staleSlot的下标位置往前找,直到Entry为null,将距离Entry为null最近且key为null的过期数据的下标,记录到slotToExpunge(如果没找到的话 slotToExpunge 还是staleSlot)
C,从staleSlot的下一个往后找,直到找到的Entry是null,(有可能碰到key为null,或者匹配的key)
(1)Entry数据 key匹配 交换当前位置与staleSlot数据的位置,并开始执行清理过期数据
(2)发现了第一个key为null的过期数据,将slotToExpunge指定到当前下标
D.找到最后也没发现key=null或者key相等的Entry,将staleSlot对应的数据的valueGc掉,同时,包装当前value到Entry中插入到staleSlot的下标中
E. D C(1)步骤均需要清理过期数据
a.清理当前下标的数据,并向后遍历知道entry为null
c.当前entry是过期数据,直接置为null
d.不是过期数据,计算应该所在的index下标位置,然后从index往后查,如果查到一个过期的数据temp的话,直接将当前entry的数据更新到 temp所对应的下标去
e.从a遍历的最后一个entry为null的下标i,往后接着尝试(次数为length最高位右移的位数)遍历 如果发现为过期数据的话走a,尝试最多次数后放弃
4..如果下标的Entry不是空,并且key不等,那么向后寻址,继续1.2的步骤
5.如果清理完过期的数据之后size还大于四份之三的阈值的话就会扩容
a。数组为原来的2倍,重新设定阈值
c.拷贝数据到新数组,有hash 碰撞的数据的话切不过期的话就往后面插入,过期的话直接释放
B。get方法
1.如果当前Thread的ThreadLocalMap还没有被初始化那么直接调用setInitialValue的方法
会先调用自身的initialValue方法,创建好Map之后返回当前value(initialValue产生的)
2.如果map不是null ,就调用getEntry的方法获取到,如果getEntry也没找到,就调用setInitialValue的放法
每一次的setget remove都会清理过期数据,并将过期数据的位置替换成本该自己的位置