1、ThreadLocal的作用為:為每一條線程分配獨立的資源赵誓,與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對象存進去的值。這也就是為什么只有當(dāng)前線程通過該ThreadLocal對象能夠訪問該資源的原因正歼,例如面代碼的t1.get()
public class ConnectionManager {
創(chuàng)建兩個當(dāng)前線程的map集合的操作對象t1和t2辐马,也即只屬于當(dāng)前線程的鍵值對,該資源為當(dāng)前線程獨享
只能通過當(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芒珠,以傳進來的自定義value作為value,增加一對該ThreadLocal對象的鍵值對
if (map != null)
map.set(this, value);
else
如果map為空搅裙,則調(diào)用createMap方法創(chuàng)建一個map集合皱卓,鍵為當(dāng)前ThreadLocal對象,值為傳進來的自定義value
createMap(t, value);
}
創(chuàng)建一個集合呈宇,以當(dāng)前ThreadLocal對象作為key好爬,以調(diào)用該方法的對象傳進來的value作為值生成一個map
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
}
最后編輯于 :
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者