前言
两个与添加元素相关的方法,initialValue()用于初始化一个默认值,set()用于添加一个元素
set()方法分析
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
用于添加一个元素的方法,传入的参数是需要保存的Value对象,即所谓的线程局部变量
1、获取当前线程
首先获取调用set()方法的当前Thread对象,并由局部变量t保存
2、获取当前线程持有的ThreadLocalMap对象
接着将当前Thread对象t传入到getMap()方法中,get()方法会返回一个ThreadLocalMap对象,ThreadLocalMap对象是一个哈希表结构的容器,每个Thread对象都会持有一个ThreadLocalMap对象,
3、检查是否成功获取到当前线程持有的ThreadLocalMap对象
getMap()方法有可能返回null,当返回null时,说明Thread对象负责持有的ThreadLocalMap对象还没有创建,所以这里对map进行了判断,分别对两种情况做了不同的业务逻辑
a、当map不等于null时,说明Thread持有的ThreadLocalMap对象已经创建,直接调用ThreadLocalMap对象的set()方法添加一个元素,传入的第一个参数是Key对象,第二个参数是Value对象,此处我们传入的Key对象是当前ThreadLocal对象,Value对象则是方法中传入的T类型的对象
b、当map为null空,则会调用一个createMap()方法用于创建ThreadLocalMap对象,createMap()方法接受的第1个参数是当前Thread对象(注意此处是线程对象,不是ThreadLocal对象),第2个参数是T类型的对象value(线程局部变量,即元素),createMap()方法创建负责缓存key-value对象的容器对象,即ThreadLocalMap对象,它的结构是哈希表,底层是数组,解决哈希冲突用了单链表,而我们添加进去的T类型的对象(元素),将作为该哈希表ThreadLocalMap对象的第一个Entry元素(key-value)中的value,其中的key就是当前ThreadLocal对象
initialValue()方法分析
protected T initialValue() {
return null;
}
用于初始化一个默认Value值的方法,一般我们都会重写该方法,注意它是protected修饰
initialValue()方法的返回值就是我们要存储的线程局部变量对象(元素),第一次调用ThreadLocal的get()方法(第三篇:获取元素)的时,initialValue()方法会返回一个Value对象
getMap()方法分析
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
用于返回Thread对象持有的ThreadLocalMap对象,传入的参数是Thread对象,一般我们都会传入当前线程
返回的数据类型是ThreadLocalMap,ThreadLocalMap作为静态内部类位于ThreadLocal类中
Thread对象持有的threadLocals
ThreadLocal.ThreadLocalMap threadLocals = null;
可以清楚的看到ThreadLocalMap是ThreadLocal中的一个静态内部类
createMap()方法分析
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
用于为Thread对象创建持有的ThreadLocalMap对象的方法,传入的第1个参数为Thread对象,传入的第2个参数为Value(第一次保存的线程局部变量)
总结
1、通过重写ThreadLocal的initialValue()方法,我们可以设置一个默认的线程局部变量对象,好处是,我们在创建ThreadLocal对象时,即保存了一个线程局部变量
2、第一次调用ThreadLocal对象的get()方法时(此时未通过set设置过线程局部变量对象),initialValue()方法返回的线程局部变量对象会存储在一个哈希表(ThreadLocalMap)中
3、通过ThreadLocal的set()方法我们可以添加一个以ThreadLocal对象为key,线程局部变量对象为value的元素到ThreadLocalMap对象中,因ThreadLocalMap对象由当前Thread对象持有,所以set()方法总是需要获得调用它的Thread对象,通过Thread.currentThread()即可得到当前线程对象(在Java虚拟机中,一个Thread对象会对应一个操作系统中的线程)
4、ThreadLocal能实现以Thread为作用域,存储一个对象,依靠的是每个Thread对象持有的一个ThreadLocalMap对象,具体的添加元素过程则是由ThreadLocalMap对象的set()方法完成的
5、当你通过ThreadLocal对象添加一个对象作为线程局部变量时,该对象的引用会存储在Thread对象持有的一个ThreadLocalMap对象中,再由ThreadLocalMap持有的数组对象负责持有
6、Thread对象持有的ThreadLocalMap对象是在ThreadLocal的createMap()方法中创建的!