1、ThreadLocal的作用為:為每一條線程分配獨(dú)立的資源,與synchronized方式不同,ThreadLocal是典型的以空間換時間的做法,而synchronized方式則是以時間換空間,ThreadLocal方式避免了多個線程之間資源共享時可能會發(fā)生的問題。
2、這個線程往往橫跨多個層(web,service,dao等),例如客戶端瀏覽器的request和respons均屬于一個線程,他們均采用的是ThreadLocal方式在多個層之間傳遞信息,也就意味著在這同一個線程的多個層可以共享同一個資源。
3、每一個線程都不能訪問其他線程ThreadLocal對象的資源,該線程的資源存儲用map方式實現(xiàn),因為獲取的ThreadLocaMap是當(dāng)前線程綁定的。該map集合的鍵為當(dāng)前線程的各個ThreadLocal對象。
4、例如下面代碼的t1和t2對象,且key不可自定義,key默認(rèn)為當(dāng)前線程當(dāng)前調(diào)用的ThreadLocal對象,只有滿足了當(dāng)前線程以及當(dāng)前對象這兩個條件后,才能通過當(dāng)前對象這個默認(rèn)的key,取出這個線程在使用這個ThreadLocal對象存進(jìn)去的值。這也就是為什么只有當(dāng)前線程通過該ThreadLocal對象能夠訪問該資源的原因,例如面代碼的t1.get()
public class ConnectionManager {
創(chuàng)建兩個當(dāng)前線程的map集合的操作對象t1和t2,也即只屬于當(dāng)前線程的鍵值對,該資源為當(dāng)前線程獨(dú)享
只能通過當(dāng)前線程下對應(yīng)的對象作為key取出value,而key是默認(rèn)的,所以可以直接寫成t1.get()
尖括號里面規(guī)定了操作的對象為Connection對象。
static ThreadLocal<Connection> t1 = new ThreadLocal<>();
static ThreadLocal<Connection> t2 = new ThreadLocal<>();
該方法返回一個當(dāng)前線程共用的connection對象
public static Connection getConnectionByThread() throws SQLException {
直接從當(dāng)前線程的map集合中取出一條連接對象
Connection connection = t1.get();
如果當(dāng)前線程的map集合中還沒有連接對象,connection的值會為null
則從連接池獲得一條連接對象,通過set方法放入到該線程專用的map集合中
if(connection==null){
connection = C3P0Util.getConnection();
t1.set(connection);
}
return connection;
}
該getConnectionByThread2方法同理,這里是為了演示一個線程可以創(chuàng)建多個ThreadLocal對象
public static Connection getConnectionByThread2() throws SQLException {
Connection connection = t2.get();
if(connection==null){
connection = C3P0Util.getConnection();
t2.set(connection);
}
return connection;
}
---------------------------------------------------------------------------------------------------------------------------
好了我們通過源碼來看看get方法都做了什么
public T get() {
首先獲取當(dāng)前線程,這是區(qū)分各線程資源的第一步
Thread t = Thread.currentThread();
接下來通過獲取到的線程t獲得當(dāng)前線程的map集合,這是之所以能區(qū)分各線程資源的原因,因為t是當(dāng)前線程,所以獲取到的ThreadLocalMap永遠(yuǎn)是本線程的。
ThreadLocalMap map = getMap(t);
接下來判斷這個獲得的map集合是否為空
if (map != null) {
如果不為空,則調(diào)用getEntry方法把當(dāng)前ThreadLocal對象作為key(也就是this)獲取該ThreadLocal對象對應(yīng)的value值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
e不為空說明存在與該this對應(yīng)的value,將其取出,賦值給result
@SuppressWarnings("unchecked")
T result = (T)e.value;
返回該結(jié)果
return result;
}
}
如果上面的兩個if都不符合,說明該key對應(yīng)的value為空或者該map集合為空,則繼續(xù)調(diào)用setInitialValue方法
return setInitialValue();
}
那么上面提到的setInitialValue方法又是何許人也?
private T setInitialValue() {
可見下面的代碼,調(diào)用initialValue方法返回的是null,并將其賦值給value
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
如果這里的map不為空說明是get()方法里面的e為空,說明沒有該對象的鍵值對
將當(dāng)前的ThreadLocal對象作為key,剛剛通過initialValue方法取得的value作為值添加到集合里面去
map.set(this, value);
else
如果map為空,則調(diào)用createMap方法生成初始化的map集合,createMap方法見代碼最后
createMap(t, value);
return value;
}
可見,調(diào)用initialValue方法返回的是一個null,也就是默認(rèn)的value值為null
這里專門寫一個initialValue用來返回一個null而不是直接給value賦值為null的目的是希望其子類覆蓋該方法,自定義該value值
protected T initialValue() {
return null;
}
---------------------------------------------------------------------------------------------------------------------------
說完ThreadLocal的get方法,我們再來看看他的set方法,set方法比get方法容易理解一些
public void set(T value) {
同樣是獲取當(dāng)前線程從而獲取當(dāng)前線程的map集合
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
如果map集合不為空,則可以直接以當(dāng)前ThreadLocal對象為key,以傳進(jìn)來的自定義value作為value,增加一對該ThreadLocal對象的鍵值對
if (map != null)
map.set(this, value);
else
如果map為空,則調(diào)用createMap方法創(chuàng)建一個map集合,鍵為當(dāng)前ThreadLocal對象,值為傳進(jìn)來的自定義value
createMap(t, value);
}
創(chuàng)建一個集合,以當(dāng)前ThreadLocal對象作為key,以調(diào)用該方法的對象傳進(jìn)來的value作為值生成一個map
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
}
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。