ThreadLocal
創(chuàng)新互聯(lián)長期為超過千家客戶提供的網(wǎng)站建設(shè)服務(wù),團(tuán)隊(duì)從業(yè)經(jīng)驗(yàn)10年,關(guān)注不同地域、不同群體,并針對(duì)不同對(duì)象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺(tái),與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為興山企業(yè)提供專業(yè)的成都網(wǎng)站建設(shè)、網(wǎng)站制作,興山網(wǎng)站改版等技術(shù)服務(wù)。擁有10多年豐富建站經(jīng)驗(yàn)和眾多成功案例,為您定制開發(fā)。
概述
一、對(duì)ThreadLocal的理解
1.1 ThreadLocal在JDK中的定義
1.2 應(yīng)用場景
二、深入分析ThreaLocal類
2.1 get()
2.2 setIntialValue()
2.3 ThreadLocal類是如何為每個(gè)線程創(chuàng)建變量副本
三、ThreadLocal的應(yīng)用場景
3.1 數(shù)據(jù)庫連接問題
3.2 Session管理
3.3 Thread-per-Request(一個(gè)請(qǐng)求對(duì)應(yīng)一個(gè)服務(wù)器線程)
四、ThreadLocal的一般使用步驟
概述
本文借鑒了Java 并發(fā)編程這篇博主專欄的文章,他的專欄寫的很好,推薦!
ThreadLocal 又名 線程局部變量 ,是 Java 中一種較為特殊的線程綁定機(jī)制,可以為每一個(gè)使用該變量的線程都提供一個(gè)變量值的副本,并且每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)與其它線程的副本發(fā)生沖突。
一般而言,通過 ThreadLocal 存取的數(shù)據(jù)總是與當(dāng)前線程相關(guān),也就是說,JVM 為每個(gè)運(yùn)行的線程綁定了私有的本地實(shí)例存取空間,從而為多線程環(huán)境常出現(xiàn)的并發(fā)訪問問題提供了一種 隔離機(jī)制 。
如果某個(gè)變量要被某個(gè)線程獨(dú)享,那么我們就可以通過ThreadLocal來實(shí)現(xiàn)本地存儲(chǔ)功能;
一、對(duì)ThreadLocal的理解
1.1 ThreadLocal在JDK中的定義
每個(gè)線程都有關(guān)于該 ThreadLocal變量 的私有值 ,并且對(duì)其他線程是不可見的;
ThreadLocal 可以給定一個(gè)獨(dú)立于變量的初始值,這樣每個(gè)線程就會(huì)獲得這個(gè)初始化值的一個(gè)拷貝,并且每個(gè)線程對(duì)這個(gè)值的修改對(duì)其他線程是不可見的;
ThreadLocal的一個(gè)重要作用是就是將類的狀態(tài)與線程關(guān)聯(lián)起來,解決方案就是在這個(gè)類中定義一個(gè) private static ThreadLocal 實(shí)例;
1.2 應(yīng)用場景
類 ThreadLocal 主要解決的就是為每個(gè)線程綁定自己的值,以方便其處理自己的狀態(tài);
形象地講,可以將 ThreadLocal變量 比喻成全局存放數(shù)據(jù)的盒子,盒子中可以存儲(chǔ)每個(gè)線程的私有數(shù)據(jù)。例如,以下類用于生成對(duì)每個(gè)線程唯一的局部標(biāo)識(shí)符。線程 ID 是在第一次調(diào)用 uniqueNum.get() 時(shí)分配的,在后續(xù)調(diào)用中不會(huì)更改;
二、深入分析ThreaLocal類
ThreadLocal中,一共提供了四個(gè)方法:
public T get() { }
public void set(T value){ }
public void remove(){ }
protected T initialValue{ }
1
2
3
4
get() 獲取ThreadLocal變量在當(dāng)前線程中保存的值;
set() 設(shè)置ThreadLocal在當(dāng)前線程的值;
remove() 用來移除當(dāng)前線程中相關(guān)ThreadLocal變量;
initialValue() 是一個(gè)protected方法,一般需要重寫;
2.1 get()
源碼如下:
public T get(){
Thread t = Thread.currentThread();//獲取當(dāng)前線程對(duì)象
ThreadLocalMap map = getMap(t);
if(map!=null){
//從當(dāng)前線程的ThreadLocalMap獲取該thread-local variable對(duì)應(yīng)的entry
ThreadLocalMap.Entry e = map.getEntry(this);
if(e!=null)
return (T)e.value;//取得目標(biāo)值
}
return setInitialValue();
}
1
2
3
4
5
6
7
8
9
10
11
2.2 setIntialValue()
private T setIntialValue(){
T value = initialValue(); //默認(rèn)實(shí)現(xiàn)返回null
Thread t = Thread.currentThread();//獲得當(dāng)前線程;
ThreadLocalMap map = getMap(t);//得到當(dāng)前線程ThreadLocalMap類型域threadLocals
if(map!=null)
map.set(this,value);//該 map 的鍵是當(dāng)前 ThreadLocal 對(duì)象
else?
createMap(t,value);
return value;
}
1
2
3
4
5
6
7
8
9
10
initialValue()
? ? protected T initialValue() {
? ? ? ? return null;? ? ? ? ? ? // 默認(rèn)實(shí)現(xiàn)返回 null
? ? }
1
2
3
createMap()
? ? void createMap(Thread t, T firstValue) {
? ? ? ? t.threadLocals = new ThreadLocalMap(this, firstValue); // this 指代當(dāng)前 ThreadLocal 變量,為 map 的鍵??
? ? }
1
2
3
2.3 ThreadLocal類是如何為每個(gè)線程創(chuàng)建變量副本
每個(gè)線程內(nèi)部有一個(gè) ThreadLocal.ThreadLocalMap 類型的成員變量 threadLocals,這個(gè) threadLocals 存儲(chǔ)了與該線程相關(guān)的所有 ThreadLocal 變量及其對(duì)應(yīng)的值(”ThreadLocal 變量及其對(duì)應(yīng)的值” 就是該Map中的一個(gè) Entry)。 Key 是 ThreadLocal 變量, Value 是該 ThreadLocal 變量對(duì)應(yīng)的值;
初始時(shí),Thread里面的threadlocals為空,當(dāng)ThreadLocal變量調(diào)用**get()方法或者set()**就會(huì)對(duì)Thread類中的threadlocals進(jìn)行初始化,并且以當(dāng)前ThreadLocal變量為鍵值,以ThreadLocal要保存的值為value,存到 threadLocals;
然后在當(dāng)前線程里面,如果要使用ThreadLocal對(duì)象,就可以通過get方法獲得該線程的threadLocals,然后以該ThreadLocal對(duì)象為鍵取得其對(duì)應(yīng)的 Value,也就是ThreadLocal對(duì)象中所存儲(chǔ)的值;
三、ThreadLocal的應(yīng)用場景
Java 中,類 ThreadLocal 解決的是變量在不同線程間的隔離性。最常見的 ThreadLocal 使用場景有 數(shù)據(jù)庫連接問題、Session管理等。
3.1 數(shù)據(jù)庫連接問題
private static ThreadLocal
? ? public Connection initialValue() {
? ? ? ? return DriverManager.getConnection(DB_URL);
? ? }
};
public static Connection getConnection() {
? ? return connectionHolder.get();
}
1
2
3
4
5
6
7
8
9
3.2 Session管理
private static final ThreadLocal threadSession = new ThreadLocal();
public static Session getSession() throws InfrastructureException {
? ? Session s = (Session) threadSession.get();
? ? try {
? ? ? ? if (s == null) {
? ? ? ? ? ? s = getSessionFactory().openSession();
? ? ? ? ? ? threadSession.set(s);
? ? ? ? }
? ? } catch (HibernateException ex) {
? ? ? ? throw new InfrastructureException(ex);
? ? }
? ? return s;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
3.3 Thread-per-Request(一個(gè)請(qǐng)求對(duì)應(yīng)一個(gè)服務(wù)器線程)
在經(jīng)典Web交互模型中,請(qǐng)求的處理基本上采用的都是“一個(gè)請(qǐng)求對(duì)應(yīng)一個(gè)服務(wù)器線程”的處理方式,因此就可以將請(qǐng)求設(shè)置成類似ThreadLocal的形式,這樣,當(dāng)某個(gè)服務(wù)器線程來處理請(qǐng)求時(shí),就可以獨(dú)享該請(qǐng)求的處理;
四、ThreadLocal的一般使用步驟
ThreadLocal使用步驟一般分為三步:
創(chuàng)建一個(gè)ThreadLocal對(duì)象threadXxx,用來保存線程間需要隔離處理的對(duì)象xxx;
提供一個(gè)獲取要隔離訪問的數(shù)據(jù)的方法 getXxx(),在方法中判斷,若 ThreadLocal對(duì)象為null時(shí)候,應(yīng)該 new() 一個(gè)隔離訪問類型的對(duì)象;
在線程類的run()方法中,通過getXxx()方法獲取要操作的數(shù)據(jù),這樣可以保證每個(gè)線程對(duì)應(yīng)一個(gè)數(shù)據(jù)對(duì)象,在任何時(shí)刻都操作的是這個(gè)對(duì)象,不會(huì)交叉。
————————————————