前面《Thread源碼理解》一節(jié)講了Thread的源碼妖啥,因為是第一次寫博客,難免有點拘泥于格式对碌,本來想下面這張寫一下我對線程池實現(xiàn)的理解荆虱,但是今天重新翻出了我的處女作,發(fā)現(xiàn)Thread源碼中有ThreadLocal的變量:
#1
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
一直以來都是把ThreadLocal當作同一線程中順序存取線程變量的容器朽们,當看到Thread類中ThreadLocal變量=null且沒有使用怀读,則Thread類中定義ThreadLocals 變量是用來做什么的?后面會詳細的分析ThreadLocals變量的用法骑脱。
一菜枷、ThreadLocalMap
首先,ThreadLocalMap是一個靜態(tài)內(nèi)部類叁丧,在ThreadLocal中定義了靜態(tài)Map啤誊,此種設(shè)計屬于組合模式(六原則一法則)。
(1)
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
-------------------------------------------
(2)
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold; // Default to 0
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
--------------------------------------------
(3)
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
1拥娄、從上面的源碼1中可以知道ThreadLocalMap是內(nèi)部數(shù)組Entry為弱引用(WeakReference)的Map蚊锹,且該引用為ThreadLocal<?>(弱引用為發(fā)生GC時清除對應(yīng)對象,該處設(shè)計的很巧妙稚瘾,認為線程執(zhí)行時間小于兩次gc間隔時間牡昆,則一個線程內(nèi)數(shù)據(jù)是能夠順序獲取的。)
2摊欠、從2中知道Entry初始大小為16丢烘,負載因子為2/3。
3些椒、在3中主要是設(shè)置線程在ThreadLocalMap對應(yīng)槽的值播瞳。
有這么一段代碼:
table[i] = new Entry(firstKey, firstValue);
因?qū)?yīng)槽中是線程的資源作為key,當線程銷毀時免糕,對應(yīng)的Entry[i]不會被銷毀狐史,而其對應(yīng)的線程已經(jīng)消失,此時需要清除這個Entry说墨,而因為采用了弱引用骏全,JVM會幫忙清除對應(yīng)Entry(這也是1中所說的設(shè)計巧妙的地方,避免產(chǎn)生內(nèi)存泄露)尼斧。
4姜贡、上面分析了Entry的實現(xiàn),那現(xiàn)在有這么一種情況棺棵,當線程為線程池中的線程時楼咳,我們知道線程池中的線程terminated的狀態(tài)后不會被銷毀熄捍,當新的請求到來時會作為其work線程繼續(xù)處理任務(wù),那此時母怜,如果該Entry的數(shù)據(jù)沒有被GC余耽,該ThreadLocal中存了上個請求的數(shù)據(jù),所以當在線程池中使用ThreadLocal時苹熏,記得清除數(shù)據(jù)碟贾。
二、ThreadLocal的實現(xiàn)
一中對ThreadLocal中的Map的實現(xiàn)進行了分析轨域,通過Map的槽存儲了以ThreadLocal為key的鍵值對袱耽,那是如何實現(xiàn)的呢?
在ThreadLocal中有兩個基本方法:
1)set方法
(1)
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
------------------------------------------------
(2)
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
------------------------------------------------
(3)
private void set(ThreadLocal<?> key, Object value) {
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();
}
------------------------------------------
(4)
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
1干发、(1)中g(shù)etMap((Thread)t)朱巨,第一次調(diào)用時,參考#1中Thread中ThreadLocals=nu l l枉长,第一次獲取map=null冀续,調(diào)用(4)new ThreadLocalMap(this, firstValue),見一(3)分析必峰。
2洪唐、后續(xù)set對應(yīng)線程值時,直接對ThreadLocalMap的槽操作自点。
3桐罕、replaceStaleEntry脉让、cleanSomeSlots這兩個方法是用來處理key為null時清除臟數(shù)據(jù)桂敛,暫時先不分析。
2)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();
}
---------------------------------------------------
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;
}
-----------------------------------------------------
protected T initialValue() {
return null;
}
get方法比較簡單溅潜,就是獲取桶中的對應(yīng)ThreadLocal的value术唬,如果為null就返回初始值(也是null,哈哈滚澜,點個贊吧)粗仓。
三、inheritableThreadLocals實現(xiàn)
前面講了ThreadLocal的實現(xiàn)设捐,同時也了解了Thread類中的ThreadLocals的作用和使用借浊。在此基礎(chǔ)上,來分析一下inheritableThreadLocals的實現(xiàn)萝招。
以下面源碼為引:
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
上面是Thread中init的實現(xiàn)蚂斤,當父inheritableThreadLocals 存在的時候,也就是子線程可繼承時槐沼,調(diào)用ThreadLocal.createInheritedMap方法曙蒸,從下面的源碼中能夠看到捌治,子ThreadLocals初始化時,是對父inheritableThreadLocals的hash桶進行了完全復(fù)制:
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
-------------------------------------------------------
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}