ThreadLocal 、ThreadLocalMap 与 Thread 的关系
ThreadLocalMap 是一个类似HashMap的结构,但并不是Map的子类
ThreadLocalMap 是 ThreadLocal 的静态内部类
Entry是ThreadLocalMap 的静态内部类,也是 WeakReference(弱引用)的子类,
Entry是弱引用类型,所以当垃圾回收器进行回收时,无论内存是否足够,都会回收该对象的内存
ThreadLocalMap 中有一个属性:Entry[] table,用来存放key-value对,一个Entry实例对应 1份key-value
Thread类有一个 ThreadLocalMap 类型的属性:threadLocals,所以每一个 threadObj 都有1个自己独有的 ThreadLocalMap
ThreadLocal --> 内部类ThreadLocalMap --> 内部类Entry
ThreadLocalMap --> 属性:Entry[] table
Thread --> 属性:ThreadLocalMap threadLocals
1个threadObj --- 1个ThreadLocalMap --- 1个Entry[] --- 多个Entry --- 多份key-value数据 --- 多个key --- 1个key对应1个threadLocalObj
往ThreadLocalMap中设值 set
threadLocalObj.set(value) --> currentThread.threadLocals.set(threadLocalObj, value)
--> 根据 threadLocalObj 运算得到一个下标i --> table[i] = new Entry(threadLocalObj, value) = entryObj
最终 value 赋值给了entryObj 的 value 属性,并创建了一个指向key(也就是threadLocalObj)的弱引用,
这个弱引用与Entry的祖父类的属性关联,所以这个弱引用是被 entryObj 持有的
从ThreadLocalMap中取值 get
threadLocalObj.get() --> currentThread.threadLocals.getEntry(threadLocalObj)
--> 根据 threadLocalObj 运算得到下标i --> 得到 table[i],拿 threadLocalObj 与 table[i] 持有的弱引用 比对
--> 一致的话,table[i].value 就是所求的值
从ThreadLocalMap中删除键值 remove
threadLocalObj.remove() --> currentThread.threadLocals.remove(threadLocalObj) --> 根据 threadLocalObj 运算得到下标i
--> tab[i].clear --> 把tab[i]持有的弱引用赋值为null,即是把键值对的key设置为null
里面还有个expungeStaleEntry方法,不仅会把threadLocalObj对应的value擦除,
而且ThreadLocalMap中所有过时的key-value对都会擦除
1个线程 -- 对应一个ThreadLocalMap -- 对应多个键值对 -- 每个键值对对应1个threadLocalObj
所以如果要往ThreadLocalMap中存放多个键值对,那么就要创建多个 threadLocalObj
key-value对最终存放在 ThreadLocalMap 的Entry数组中
假设有a,b两个请求 --- 对应t1,t2两个线程 --- 对应ThreadLocalMap1,ThreadLocalMap2
如果要往ThreadLocalMap中保存两个值value1和value2,那么则需要两个 threadLocalObj 作为key,
ThreadLocalMap1中的数据:threadLocalObj1:value1-t1 threadLocalObj2:value2-t1
ThreadLocalMap2中的数据:threadLocalObj1:value1-t2 threadLocalObj2:value2-t2
ThreadLocalMap 的生命周期:
ThreadLocalMap实例 随着 threadObj 第一次调用 threadLocalObj.set(value) 、threadLocalObj.get()时诞生,随着 threadObj 销毁而销毁。
threadLocalObj 直接关系到 key-value对 在 ThreadLocalMap 中的存放位置
因为每个线程都有自己的 ThreadLocalMap,所以使用同一个 threadLocalObj 并不会影响它们设值和取值
比如下面,ThreadLocal 是单例的,但 ThreadLocalMap 不是