在日常開發(fā)當(dāng)中,我們會(huì)將變量當(dāng)做參數(shù)一層層傳遞下去,但是突然有一天業(yè)務(wù)邏輯變了警医,在一個(gè)很深的地方需要加一個(gè)參數(shù),這樣你就要一層一層的往上加坯钦,確保這個(gè)參數(shù)能傳到調(diào)用的地方预皇。無疑,這是一件很糟糕的事情葫笼。而使用ThreadLocal可以解決這個(gè)問題
ThreadLocal的中文翻譯叫:線程局部變量深啤。
ThreadLocal的使用原因
他不是一個(gè)線程,而是一個(gè)線程的本地化對(duì)象路星。當(dāng)某個(gè)變量在使用ThreadLocal進(jìn)行維護(hù)時(shí)溯街,ThreadLocal為使用該變量的每個(gè)線程分配了一個(gè)獨(dú)立的變量副本,每個(gè)線程可以自行操作自己對(duì)應(yīng)的變量副本洋丐,而不會(huì)影響其他線程的變量副本呈昔。
通過ThreadLocal存取的數(shù)據(jù),總是與當(dāng)前線程相關(guān)友绝,也就是說堤尾,JVM 為每個(gè)運(yùn)行的線程,綁定了私有的本地實(shí)例存取空間迁客,從而為多線程環(huán)境常出現(xiàn)的并發(fā)訪問問題提供了一種隔離機(jī)制
ThreadLocal的實(shí)現(xiàn)
在ThreadLocal類中有一個(gè)Map郭宝,用于存儲(chǔ)每一個(gè)線程的變量的副本。ThreadLocal主要提供get()
和set()
兩個(gè)方法掷漱。
set方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap
ThreadLocalMap
是ThreadLocal
的一個(gè)靜態(tài)內(nèi)部類粘室,它實(shí)現(xiàn)了鍵值對(duì)的設(shè)置和獲取(類似于 Map<K,V> 存儲(chǔ)的key-value)卜范,每個(gè)線程中都有一個(gè)獨(dú)立的ThreadLocalMap
副本衔统,它所存儲(chǔ)的值,只能被當(dāng)前線程讀取和修改海雪。ThreadLocal類通過操作每一個(gè)線程特有的 ThreadLocalMap
副本锦爵,從而實(shí)現(xiàn)了變量訪問在不同線程中實(shí)現(xiàn)隔離。因?yàn)槊總€(gè)線程的變量都是自己特有的奥裸,完全不會(huì)有并發(fā)錯(cuò)誤险掀。還有一點(diǎn)就是,ThreadLocalMap
存儲(chǔ)的鍵值對(duì)中的鍵是this對(duì)象指向的ThreadLocal
對(duì)象湾宙,而值就是你所設(shè)置的對(duì)象了
ThreadLocalMap的set方法:
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();
}
getMap和creatMap的實(shí)現(xiàn):
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
通過源碼分析可以看出迷郑,通過獲取和設(shè)置 Thread內(nèi)的threadLocals
變量枝恋,而這個(gè)變量的類型就是 ThreadLocalMap
,這樣進(jìn)一步驗(yàn)證了上文中的觀點(diǎn):每個(gè)線程都有自己獨(dú)立的ThreadLocalMap
對(duì)象嗡害。打開java.lang.Thread
類的源代碼,我們能得到更直觀的證明:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
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;
}
在獲取和當(dāng)前線程綁定的值時(shí)畦攘,ThreadLocalMap對(duì)象是以 this 指向的 ThreadLocal 對(duì)象為鍵進(jìn)行查找的霸妹,set() 方法是設(shè)置變量的拷貝副本,get() 方法通過鍵值對(duì)的方式獲取到這個(gè)本地變量的副本的value知押。
remove源碼:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
該方法就是通過 this 找到ThreadLocalMap 中保存的變量副本做回收處理