ThreadLocal的介绍与运用( 五 )

(2 )代码执行流程
? A. 首先获取当前线程
? B. 根据当前线程获取一个Map
? C. 如果获取的Map不为空,则在Map中以ThreadLocal的引用作为key来在Map中获取对应的value e , 否则转到E
? D. 如果e不为null,则返回e.value,否则转到E
? E. Map为空或者e为空 , 则通过initialValue函数获取初始值value , 然后用ThreadLocal的引用和value作为firstKey和firstValue创建一个新的Map
总结:先获取当前线程的 ThreadLocalMap 变量 , 如果存在则返回值 , 不存在则创建并返回初始值 。
4.2 set方法(1 ) 源码和对应的中文注释
/*** 设置当前线程对应的ThreadLocal的值** @param value 将要保存在当前线程对应的ThreadLocal的值*/public void set(T value) {// 获取当前线程对象Thread t = Thread.currentThread();// 获取此线程对象中维护的ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 如果此map存在if (map != null)// 存在则调用map.set设置此实体entrymap.set(this, value);else// 1)当前线程Thread 不存在ThreadLocalMap对象// 2)则调用createMap进行ThreadLocalMap对象的初始化// 3)并将此实体entry作为第一个值存放至ThreadLocalMap中createMap(t, value);}(2 )代码执行流程
? A. 首先获取当前线程,并根据当前线程获取一个Map
【ThreadLocal的介绍与运用】? B. 如果获取的Map不为空,则将参数设置到Map中(当前ThreadLocal的引用作为key)
? C. 如果Map为空,则给该线程创建 Map,并设置初始值
4.3 remove方法(1 ) 源码和对应的中文注释
/*** 删除当前线程中保存的ThreadLocal对应的实体entry*/public void remove() {// 获取当前线程对象中维护的ThreadLocalMap对象ThreadLocalMap m = getMap(Thread.currentThread());// 如果此map存在if (m != null)// 存在则调用map.remove// 以当前ThreadLocal为key删除对应的实体entrym.remove(this);}(2 )代码执行流程
? A. 首先获取当前线程,并根据当前线程获取一个Map
? B. 如果获取的Map不为空,则移除当前ThreadLocal对象对应的entry
4.4 initialValue方法/*** 返回当前线程对应的ThreadLocal的初始值* 此方法的第一次调用发生在,当线程通过{@link #get}方法访问此线程的ThreadLocal值时* 除非线程先调用了 {@link #set}方法 , 在这种情况下,* {@code initialValue} 才不会被这个线程调用 。* 通常情况下,每个线程最多调用一次这个方法 。** <p>这个方法仅仅简单的返回null {@code null};* 如果程序员想ThreadLocal线程局部变量有一个除null以外的初始值,* 必须通过子类继承{@code ThreadLocal} 的方式去重写此方法* 通常, 可以通过匿名内部类的方式实现** @return 当前ThreadLocal的初始值*/protected T initialValue() {return null;}? 此方法的作用是 返回该线程局部变量的初始值 。
(1) 这个方法是一个延迟调用方法,从上面的代码我们得知,在set方法还未调用而先调用了get方法时才执行,并且仅执行1次 。
(2)这个方法缺省实现直接返回一个null
(3)如果想要一个除null之外的初始值 , 可以重写此方法 。(备注: 该方法是一个protected的方法 , 显然是为了让子类覆盖而设计的)
5. ThreadLocalMap源码分析5.1 基本结构? ThreadLocalMap是ThreadLocal的内部类,没有实现Map接口,用独立的方式实现了Map的功能,其内部的Entry也是独立实现 。

ThreadLocal的介绍与运用

文章插图
(1) 成员变量
/*** 初始容量 —— 必须是2的整次幂*/private static final int INITIAL_CAPACITY = 16;/*** 存放数据的table,Entry类的定义在下面分析* 同样,数组长度必须是2的冥 。*/private Entry[] table;/*** 数组里面entrys的个数,可以用于判断table当前使用量是否超过负因子 。*/private int size = 0;/*** 进行扩容的阈值,表使用量大于它的时候进行扩容 。*/private int threshold; // Default to 0/*** 阈值设置为长度的2/3*/private void setThreshold(int len) {threshold = len * 2 / 3;}(2) 存储结构 - Entry
// 在ThreadLocalMap中,也是用Entry来保存K-V结构数据的 。但是Entry中key只能是ThreadLocal对象 , 这点被Entry的构造方法已经限定死了// 另外,Entry继承WeakReference,使用弱引用,可以将ThreadLocal对象的生命周期和线程生命周期解绑,持有对ThreadLocal的弱引用,可以使得ThreadLocal在没有其他强引用的时候被回收掉 , 这样可以避免因为线程得不到销毁导致ThreadLocal对象无法被回收static class Entry extends WeakReference<ThreadLocal> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal k, Object v) {super(k);value = https://www.huyubaike.com/biancheng/v;}}

推荐阅读