1、簡介
在上一篇 ThreadLocal詳解 中,我們詳細介紹了ThreadLocal原理及設(shè)計,從源碼層面上分析了ThreadLocal。但由于ThreadLocal設(shè)計之初就是為了綁定當前線程,如果希望當前線程的ThreadLocal能夠被子線程使用,實現(xiàn)方式就會相當困難(需要用戶自己在代碼中傳遞)。在此背景下,InheritableThreadLocal應運而生。
Inheritable thread-local variables are used in preference to ordinary thread-local variables when the per-thread-attribute being maintained in the variable (e.g., User ID, Transaction ID) must be automatically transmitted to any child threads that are created.
2、應用
調(diào)用鏈追蹤:在調(diào)用鏈系統(tǒng)設(shè)計中,為了優(yōu)化系統(tǒng)運行速度,會使用多線程編程,為了保證調(diào)用鏈ID能夠自然的在多線程間傳遞,需要考慮ThreadLocal傳遞問題(大多數(shù)系統(tǒng)會使用線程池技術(shù),這已經(jīng)不僅僅是InheritableThreadLocal能夠解決的了,我會在另外一篇文章中介紹相關(guān)技術(shù)實現(xiàn))。
3、InheritableThreadLocal類
InheritableThreadLocal類重寫了ThreadLocal的3個函數(shù):
/**
* 該函數(shù)在父線程創(chuàng)建子線程,向子線程復制InheritableThreadLocal變量時使用
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* 由于重寫了getMap,操作InheritableThreadLocal時,
* 將只影響Thread類中的inheritableThreadLocals變量,
* 與threadLocals變量不再有關(guān)系
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* 類似于getMap,操作InheritableThreadLocal時,
* 將只影響Thread類中的inheritableThreadLocals變量,
* 與threadLocals變量不再有關(guān)系
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
注意:由于重寫了getMap()和createMap()兩個函數(shù),所以當
4、線程間傳值實現(xiàn)原理
說到InheritableThreadLocal,還要從Thread類說起:
public class Thread implements Runnable {
......(其他源碼)
/*
* 當前線程的ThreadLocalMap,主要存儲該線程自身的ThreadLocal
*/
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal,自父線程集成而來的ThreadLocalMap,
* 主要用于父子線程間ThreadLocal變量的傳遞
* 本文主要討論的就是這個ThreadLocalMap
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
......(其他源碼)
}
Thread類中包含 threadLocals 和 inheritableThreadLocals 兩個變量,其中 inheritableThreadLocals 即主要存儲可自動向子線程中傳遞的ThreadLocal.ThreadLocalMap。
接下來看一下父線程創(chuàng)建子線程的流程,我們從最簡單的方式說起:
4.1、用戶創(chuàng)建Thread
Thread thread = new Thread();
4.2、Thread創(chuàng)建
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, null, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*/
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
4.3、Thread初始化
/**
* 默認情況下,設(shè)置inheritThreadLocals可傳遞
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
/**
* 初始化一個線程.
* 此函數(shù)有兩處調(diào)用,
* 1、上面的 init(),不傳AccessControlContext,inheritThreadLocals=true
* 2、傳遞AccessControlContext,inheritThreadLocals=false
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
......(其他代碼)
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
......(其他代碼)
}
可以看到,采用默認方式產(chǎn)生子線程時,inheritThreadLocals=true;若此時父線程inheritableThreadLocals不為空,則將父線程inheritableThreadLocals傳遞至子線程。
4.4、ThreadLocal.createInheritedMap
讓我們繼續(xù)追蹤createInheritedMap:
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
/**
* 構(gòu)建一個包含所有parentMap中Inheritable ThreadLocals的ThreadLocalMap
* 該函數(shù)只被 createInheritedMap() 調(diào)用.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
// ThreadLocalMap 使用 Entry[] table 存儲ThreadLocal
table = new Entry[len];
// 逐一復制 parentMap 的記錄
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) {
// 可能會有同學好奇此處為何使用childValue,而不是直接賦值,
// 畢竟childValue內(nèi)部也是直接將e.value返回;
// 個人理解,主要為了減輕閱讀代碼的難度
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++;
}
}
}
}
從ThreadLocalMap可知,子線程將parentMap中的所有記錄逐一復制至自身線程。
5、總結(jié)
InheritableThreadLocal主要用于子線程創(chuàng)建時,需要自動繼承父線程的ThreadLocal變量,方便必要信息的進一步傳遞。