get方法獲取在當前線程中以ThreadLocal對象爲key的線程局部變量對象
0、無參
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();
}
首先得到調用該get方法的Thread對象,並由臨時變量t負責存儲,接着將Thread對象t傳入一個getMap方法中(見1號知識點),該方法會返回一個Thread對象持有的ThreadLocalMap對象,接着由局部變量map負責臨時存儲ThreadLocalMap對象,若Thread對象持有的ThreadLocalMap對象未創建,則getMap方法是會返回null的,所以做了以下判斷
a、當map得到的是null時,說明當前Thread對象持有的ThreadLocalMap對象還未創建,則會調用一個setInitialValue方法(見3號知識點),setInitialValue方法的返回值會最終作爲get方法的返回值
b、當map成功獲取當前Thread對象持有的ThreadLocalMap對象時,則會先調用map的getEntry方法(見4號知識點)獲取一個對象,getEntry方法接受一個當前的ThreadLocal對象,該調用將會返回一個ThreadLocalMap.Entry對象或者代表沒有匹配元素的null,返回的對象將由臨時變量e持有,所以這裏對兩種情況均做了處理
第一:e得到的是null,此時則會走到最下方的setInitialValue方法中(見3號知識點),setInitialValue的返回值將會作爲get方法的最終返回值(返回的是線程局部變量對象)
第二:若e不是null,就取出ThreadLocal.Entry對象e持有的一個Object對象value,然後將value向下轉型爲實際類型T,再賦值給局部變量result進行存儲,最後則會返回result保存的對象,該result保存的就是我們存儲的線程局部變量對象(也稱線程全局變量,在本線程內角度看的時候)
1、一個參數,接受一個Thread對象
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
返回Thread對象t持有的一個ThreadLocalMap對象(見2號知識點)
2、字段(注意:該字段位於Thread類中)
ThreadLocal.ThreadLocalMap threadLocals = null;
每個Thread對象持有了一個實例變量threadLocals,該變量的類型是ThreadLocal.ThreadLocalMap,其中ThreadLocalMap爲ThreadLocal下的靜態內部類
3、無參
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;
}
有兩種情況該方法會被調用(兩個邏輯均位於get方法中(見0號知識點))
第一:獲取線程局部變量對象時,用於存儲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號知識點)的返回值
4、一個參數,接受一個ThreadLocal對象(該方法位於ThreadLocalMap類中,ThreadLocalMap類爲ThreadLocal的靜態內部類)
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)
5、無參
protected T initialValue() {
return null;
}
該方法的返回值會作爲一個初始存儲的線程局部變量對象,此處用protcted修飾,明擺着是要你去子類中重寫該方法,這樣當該方法被調用時,就能返回一個你定義好的線程局部變量對象了,當然也可以不重寫,默認的線程局部變量對象是null就是了…………
6、兩個參數,接受一個ThreadLocal對象,一個Object對象(該方法位於ThreadLocalMap中)
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();
}
向哈希表中插入元素的方法(單獨分析)
7、兩個參數,接受一個Thread對象,接受一個線程局部變量對象T
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
首先爲傳入的Thread對象t中持有的threadLocals實例變量進行初始化工作,此處會new一個ThreadLocalMap對象,調用ThreadLocalMap的構造方法時,爲其傳入當前的ThreadLocal對象,再將傳入進來的T類型的線程局部變量對象firstValue也傳進去,創建好的ThreadLocalMap對象將由Thread對象的實例變量threadLocals持有。說明該方法的就是負責幫當前Thread對象持有的ThreadLocalMap對象進行的初始化工作
總結:
a、Thread對象持有一個ThreadLocalMap對象,它作爲哈希表對象,負責在內存中持有以ThreadLocal對象爲key,線程局部變量對象爲value的ThreadLocal.Entry對象
b、每個Thread對象持有的ThreadLocalMap對象,是在ThreadLocal類中進行的初始化,具體位置就是在ThreadLocal中的createMap方法中
c、你可以通過繼承ThreadLocal類,重寫initialValue方法,這樣就可以設置一個默認存儲的線程局部變量對象,即創建完ThreadLocal對象後,直接通過get方法獲取的對象一定是initialValue方法的返回值
d、在ThreadLocal內部,實現了一個ThreadLocalMap的靜態內部類,該類是容器類,結構是哈希表