Java并發程式設計之ThreadLocal
引言: ThreadLocal 是 JDK底層提供的一個解決多線程并發問題的工具類,它為每個線程提供了一個本地的副本變量機制,實作了和其它線程隔離,并且這種變量隻在本線程的生命周期内起作用,可以減少同一個線程内多個方法之間的公共變量傳遞的複雜度。
簡介: ThreadLocal類并不是用來解決多線程環境下的共享變量問題,而是用來提供線程内部的共享變量,在多線程環境下,可以保證各個線程之間的變量互相隔離、互相獨立。線上程中,可以通過get()和set()方法來通路和設定變量。ThreadLocal執行個體通常來說都是private static類型的,它們希望将狀态與線程進行關聯。這種變量線上程的生命周期内起作用,可以減少同一個線程内多個函數或者元件之間一些公共變量的傳遞的複雜度。
實作原理: Thread類中有兩個變量threadLocals和inheritableThreadLocals,二者都是ThreadLocal内部類ThreadLocalMap類型的變量,我們通過檢視内部類ThreadLocalMap可以發現實際上它類似于一個HashMap。在預設情況下,每個線程中的這兩個變量都為null,當線程第一次調用ThreadLocal的set或者get方法的時候才會建立他們,每個線程的本地變量不是存放在ThreadLocal執行個體中,而是放在調用線程的ThreadLocals變量裡面(該變量是Thread類的變量),也就是說,ThreadLocal類型的本地變量是存放在具體的線程空間上,其本身相當于一個裝載本地變量的工具殼,通過set方法将value添加到調用線程的threadLocals中,當調用線程調用get方法時候能夠從它的threadLocals中取出變量。如果調用線程一直不終止,那麼這個本地變量将會一直存放在他的threadLocals中,是以不使用本地變量的時候需要調用remove方法将threadLocals中删除不用的本地變量。
主要方法:
1、set()方法:在下面的代碼中,(2)處調用getMap方法獲得目前線程對應的threadLocals, 如果調用getMap方法傳回值不為null,就直接将value值設定到threadLocals中(key為目前線程引用,值為本地變量);如果getMap方法傳回null說明是第一次調用set方法(前面說到過,threadLocals預設值為null,隻有調用set方法的時候才會建立map),這個時候就需要調用createMap方法建立threadLocals。
public void set(T value) {
//(1)擷取目前線程(調用者線程)
Thread t = Thread.currentThread();
//(2)以目前線程作為key值,去查找對應的線程變量,找到對應的map
ThreadLocalMap map = getMap(t);
//(3)如果map不為null,就直接添加本地變量,key為目前線程,值為添加的本地變量值
if (map != null)
map.set(this, value);
//(4)如果map為null,說明首次添加,需要首先建立出對應的map
else
createMap(t, value);
}
2、get()方法:在get方法的實作中,首先擷取目前調用者線程,如果目前線程的threadLocals不為null,就直接傳回目前線程綁定的本地變量值,否則執行setInitialValue方法初始化threadLocals變量。在setInitialValue方法中,類似于set方法的實作,都是判斷目前線程的threadLocals變量是否為null,是則添加本地變量(這個時候由于是初始化,是以添加的值為null),否則建立threadLocals變量,同樣添加的值為null。
public T get() {
//(1)擷取目前線程
Thread t = Thread.currentThread();
//(2)擷取目前線程的threadLocals變量
ThreadLocalMap map = getMap(t);
//(3)如果threadLocals變量不為null,就可以在map中查找到本地變量的值
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
//(4)執行到此處,threadLocals為null,調用該更改初始化目前線程的threadLocals變量
return setInitialValue();
}
private T setInitialValue() {
//protected T initialValue() {return null;}
T value = initialValue();
//擷取目前線程
Thread t = Thread.currentThread();
//以目前線程作為key值,去查找對應的線程變量,找到對應的map
ThreadLocalMap map = getMap(t);
//如果map不為null,就直接添加本地變量,key為目前線程,值為添加的本地變量值
if (map != null)
map.set(this, value);
//如果map為null,說明首次添加,需要首先建立出對應的map
else
createMap(t, value);
return value;
}
3、remove()方法:remove方法判斷該目前線程對應的threadLocals變量是否為null,不為null就直接删除目前線程中指定的threadLocals變量。
public void remove() {
//擷取目前線程綁定的threadLocals
ThreadLocalMap m = getMap(Thread.currentThread());
//如果map不為null,就移除目前線程中指定ThreadLocal執行個體的本地變量
if (m != null)
m.remove(this);
}
ThreadLocal和synchronized的差別: ThreadLocal和synchronized都是用來處理多線程環境下并發通路變量的問題,隻是二者處理的角度不同、思路不同。
1、ThreadLocal 是一個類,通過對目前線程中的局部變量操作來解決不同線程的變量通路的沖突問題。是以ThreadLocal提供了線程安全的共享對象機制,每個線程都擁有其副本。
2、Java中的synchronized是一個保留字,它依靠JVM的鎖機制來實作臨界區的函數或者變量的通路中的原子性。在同步機制中,通過對象的鎖機制保證同一時間隻有一個線程通路變量。此時,被用作“鎖機制”的變量時多個線程共享的。
3、同步機制(synchronized關鍵字)采用了以“時間換空間”的方式,提供一份變量,讓不同的線程排隊通路。而ThreadLocal采用了“以空間換時間”的方式,為每一個線程都提供一份變量的副本,進而實作同時通路而互不影響。
本文參考
本文主要參考以下文章,謹以技術分享為目的,将此文搬到CSDN上,如有侵權問題請聯系本人,樂于分享提高。
作者: miaoLoveCode
連結:https://www.cnblogs.com/fsmly/p/11020641.html