前言
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()方法中建立的