前言
get()方法用于获取当前线程中以ThreadLocal对象为Key对象的线程局部变量
get()方法分析
public T get() {
Thread t = Thread.currentThread(); //当前线程对象
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
用于获取线程局部变量的方法
1、获取当前线程对象
首先得到调用该get方法的Thread对象,并由临时变量t负责存储
2、获取当前Thread对象持有的ThreadLocalMap对象
接着将Thread对象t传入一个getMap()方法中,该方法会返回一个Thread对象持有的ThreadLocalMap对象,接着由局部变量map负责临时存储ThreadLocalMap对象
3、检查Therad对象是否持有一个ThreadLocalMap对象
若Thread对象持有的ThreadLocalMap对象未创建,则getMap()方法会返回null,所以做出以下判断
a、当map得到的是null时,说明当前Thread对象持有的ThreadLocalMap对象还未创建,则会调用一个setInitialValue()方法,setInitialValue()方法的返回值最终成为get()方法的返回值
b、当map获取到当前Thread对象持有的ThreadLocalMap对象时,则先调用map的getEntry()方法获取一个对象,getEntry()方法接受一个当前的ThreadLocal对象,该调用将会返回一个ThreadLocalMap.Entry对象或者代表没有匹配元素的null,返回的对象将由临时变量e持有,所以这里对两种情况均做了处理
第一:e得到的是null,此时则会走到最下方的setInitialValue()方法中(见3号知识点),setInitialValue的返回值将会作为get方法的最终返回值(返回的是线程局部变量对象)
第二:若e不是null,就取出ThreadLocal.Entry对象e持有的一个Object对象value,然后将value向下转型为实际类型T,再赋值给局部变量result进行存储,最后则会返回result保存的对象,该result保存的就是我们存储的线程局部变量对象(也称线程全局变量,在本线程内角度看的时候)
setInitialValue()方法分析
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
有两种情况该set()方法会被调用
第一:获取线程局部变量对象时,用于存储key-value的ThreadLocalMap对象还未创建时,该方法会被调用
第二:获取线程局部变量对象时,在当前Thread对象持有的ThreadLocalMap对象中,通过ThreadLocal对象作为key,没有查找到匹配的ThreadLocalMap.Entry对象,该方法会被调用
在该方法内部,首先会调用initialValue方法(见5号知识点),该方法用于返回一个作为默认的线程局部变量对象,随后会将该值交由局部变量value进行持有,接着调用Thread的静态方法currentThread(),获得当前访问该方法的Thread对象,并由局部变量t持有,然后会把当前Thread对象t传入到getMap方法中(见1号知识点),该方法返回的ThreadLocalMap对象由局部变量map进行存储,map的值有两种情况
第一种情况:map指向的对象已经创建,此时map不为null,马上调用它的set方法(),set方法(见6号知识点)是哈希表插入元素的方法,接受两个参数,一个就是当前的ThreadLocal对象,另一个就是通过initialValue方法(见5号知识点)得到的默认局部局部变量对象
第二种情况:map为null,此时则会调用一个createMap方法(见7号知识点),同样也是把当前ThreadLocal对象作为key,默认的Value对象作为value
最后该方法会返回线程局部变量对象value,而返回值value对象则会成为get方法(见0号知识点)的返回值
getEntry()方法分析(此方法位于ThreadLocalMap类中)
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
传入的ThreadLocal对象作为key,首先获得key的hashCode值,该值正是由ThreadLocal对象持有的threadLocalHashCode实例变量保存着,接着将获得key的hashCode值与(底层数组长度-1)进行一个按位与运算,计算出的值正是桶的地址(哈希地址),该值交由临时变量i进行存储,将桶的下标i传入到底层数组对象table中得到的Entry对象,由变量e进行保管。
熟悉的代码告诉我,ThreadLocalMap是哈希表结构,它的底层数组容量也一定是2的n次方,只有这样按位与运算才等同于取模运算!(详情见HashMap)
set()方法分析(注意:这是ThreadLocalMap的set()方法,不是TheradLocal的set()方法)
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
TheradLocalMap的set()方法用于添加元素,此处省略了……将在ThreadLocalMap中分析
总结
1、Thread对象持有的ThreadLocalMap对象,它是一个哈希表结构的对象,在ThreadLocalMap持有的数组对象中,存储着以ThreadLocal对象为Key对象,线程局部变量对象为Value对象的ThreadLocal.Entry对象
2、每个Thread对象持有的ThreadLocalMap对象threadLocals,是在ThreadLocal类中的createMap()方法中创建的