大慶開發(fā)網(wǎng)站公司每日新聞
目錄
一、ThreadLocal類基本介紹
? ? ? ? 1.概述 :?
? ? ? ? 2.作用及特定 :?
二、ThreadLocal類源碼解讀
? ? ? ? 1.代碼準(zhǔn)備 :?
? ? ? ? ? ? 1.1 圖示?
? ? ? ? ? ? 1.2 數(shù)據(jù)對(duì)象
? ? ? ? ? ? 1.3 測(cè)試類
? ? ? ? ? ? 1.4 運(yùn)行測(cè)試
? ? ? ? 2.源碼分析 :?
? ? ? ? ? ? 2.1 set方法解讀
? ? ? ? ? ? 2.2 get方法解讀
一、ThreadLocal類基本介紹
? ? ? ? 1.概述 :?
? ? ? ? (1)?ThreadLocal,本地線程變量,是Java中的一個(gè)類。ThreadLocal類提供了一種線程綁定機(jī)制,可以將狀態(tài)與線程(Thread)關(guān)聯(lián)起來(lái)。ThreadLocal類如下圖所示 :?
? ? ? ? (2) 每個(gè)線程都會(huì)有自己獨(dú)立的一個(gè)ThreadLocal變量,該變量對(duì)其他線程而言是封閉且隔離的,因此對(duì)該變量的讀寫操作只會(huì)影響到當(dāng)前執(zhí)行線程的這個(gè)變量,而不會(huì)影響到其他線程的同名變量。
? ? ? ? 2.作用及特定 :?
? ? ? ? (1) ThreadLocal可以實(shí)現(xiàn)在同一個(gè)線程數(shù)據(jù)共享,從而解決多線程數(shù)據(jù)安全問(wèn)題。
? ? ? ? (2) 通過(guò)ThreadLocal類實(shí)例的set方法,可以為當(dāng)前線程關(guān)聯(lián)一個(gè)數(shù)據(jù)(變量,對(duì)象,數(shù)組)。
? ? ? ? (3) 通過(guò)ThreadLocal類實(shí)例的get方法,可以像Map一樣存取key-value鍵值對(duì)(其中key為當(dāng)前線程),注意,顯式的用法上與Map不相同。
? ? ? ? (4) 每一個(gè)ThreadLocal對(duì)象,只能為當(dāng)前線程關(guān)聯(lián)一個(gè)數(shù)據(jù),若想為當(dāng)前線程關(guān)聯(lián)多個(gè)數(shù)據(jù),就需要使用到多個(gè)ThreadLocal實(shí)例。
? ? ? ? (5) ThreadLocal實(shí)例往往定義為static類型。
? ? ? ? (6) ThreadLocal中保存的數(shù)據(jù),會(huì)在線程銷毀后自動(dòng)釋放。
二、ThreadLocal類源碼解讀
? ? ? ? 1.代碼準(zhǔn)備 :?
? ? ? ? ? ? 1.1 圖示?
? ? ? ? ? ? ? ? 首先,我們要把代碼打通,確保ThreadLocal對(duì)象可以在同一線程中實(shí)現(xiàn)數(shù)據(jù)共享。根據(jù)下圖來(lái)定義所需要的測(cè)試類 :?
? ? ? ? ? ? ? ? 在T1類,T1Service類,以及T2DAO類中分別打印出當(dāng)前線程的名字,以及放入到threadLocal1對(duì)象中的數(shù)據(jù)對(duì)象,對(duì)比三個(gè)類打印出的線程名字和數(shù)據(jù)對(duì)象是否相同,即可驗(yàn)證“ThreadLocal可以實(shí)現(xiàn)在同一個(gè)線程數(shù)據(jù)共享”。
? ? ? ? ? ? 1.2 數(shù)據(jù)對(duì)象
? ? ? ? ? ? ? ? 定義Apple類和Grape類用作測(cè)試的數(shù)據(jù)對(duì)象。
? ? ? ? ? ? ? ? Apple類代碼如下 :?
package threadlocal;public class Apple {
}
? ? ? ? ? ? ? ? Grape類代碼如下 :?
package threadlocal;/*** @author : Cyan_RA9* @version : 21.0*/
public class Grape {
}
? ? ? ? ? ? 1.3 測(cè)試類
? ? ? ? ? ? ? ? T1類代碼如下 :?
package threadlocal;/*** @author : Cyan_RA9* @version : 21.0*/
public class T1 {//定義一個(gè)靜態(tài)的ThreadLocal對(duì)象public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();public static void main(String[] args) {//在主線程中啟動(dòng)一個(gè)新的子線程new Thread(new Task()).start();}public static class Task implements Runnable{@Overridepublic void run() {System.out.println("(Task)run方法,當(dāng)前線程名 = " + Thread.currentThread().getName());Apple apple = new Apple();Grape grape = new Grape();//向threadLocal1對(duì)象中放入一個(gè)Apple對(duì)象System.out.println("(Task)run方法,放入的對(duì)象 = " + apple);threadLocal1.set(apple);new T1Service().test();}}
}
? ? ? ? ? ? ? ? T1Service類代碼如下 :?
package threadlocal;/*** @author : Cyan_RA9* @version : 21.0*/
public class T1Service {public void test() {String name = Thread.currentThread().getName();System.out.println("(T1Service)當(dāng)前線程名 = " + name);Object o = T1.threadLocal1.get();System.out.println("(T1Service)得到的對(duì)象o = " + o);new T2DAO().test();}
}
? ? ? ? ? ? ? ? T2DAO類代碼如下 :?
package threadlocal;/*** @author : Cyan_RA9* @version : 21.0*/
public class T2DAO {public void test() {String name = Thread.currentThread().getName();System.out.println("(T2DAO)當(dāng)前線程名 = " + name);Object o = T1.threadLocal1.get();System.out.println("(T2DAO)得到的對(duì)象o = " + o);}
}
? ? ? ? ? ? 1.4 運(yùn)行測(cè)試
? ? ? ? ? ? ? ? 運(yùn)行結(jié)果 :?
? ? ? ? 2.源碼分析 :?
? ? ? ? ? ? 2.1 set方法解讀
? ? ? ? ? ? ? ? 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);}}
? ? ? ? ? ? ? ? 第一步,可以看到,set方法中首先就獲取到了當(dāng)前線程,而當(dāng)前線程,就是調(diào)用set方法時(shí)——線程類run方法所在的那個(gè)線程;說(shuō)明set方法和當(dāng)前線程是關(guān)聯(lián)的。
? ? ? ? ? ? ? ? 第二步,通過(guò)當(dāng)前線程對(duì)象獲取到了ThreadLocalMap對(duì)象。此處的ThreadLocalMap,是ThreadLocal類的一個(gè)靜態(tài)內(nèi)部類。如下圖所示 :?
? ? ? ? ? ? ? ? 注意,為什么是通過(guò)當(dāng)前線程對(duì)象來(lái)獲取ThreadLocalMap對(duì)象呢?
? ? ? ? ? ? ? ? 因?yàn)楫?dāng)前線程持有自己的ThreadLocal對(duì)象(該對(duì)象調(diào)用了set方法),而ThreadLocalMap又是ThreadLocal的一個(gè)內(nèi)部類.
????????????????繼續(xù),接著判斷得到的map對(duì)象是否為空,如下圖所示 :?
? ? ? ? ? ? ? ? 如果不為空,就將當(dāng)前的ThreadLocal對(duì)象(this,即指在T1類中一開始調(diào)用set方法的ThreadLocal對(duì)象)和該對(duì)象調(diào)用set方法時(shí)放入的數(shù)據(jù)(value,此處是放入的Apple對(duì)象)。從這里也可以看出,如果同一個(gè)ThreadLocal對(duì)象再次調(diào)用set方法,會(huì)對(duì)存入的數(shù)據(jù)(value)進(jìn)行替換——即"每一個(gè)ThreadLocal對(duì)象,只能為當(dāng)前線程關(guān)聯(lián)一個(gè)數(shù)據(jù)"。
? ? ? ? ? ? ? ? 如果為空,就創(chuàng)建一個(gè)與當(dāng)前線程對(duì)象關(guān)聯(lián)的ThreadLocalMap對(duì)象,并將目標(biāo)數(shù)據(jù)放入(value)。
? ? ? ? ? ? ? ? 在set方法調(diào)用處設(shè)一個(gè)斷點(diǎn),進(jìn)入Debug界面后可以看到當(dāng)前線程對(duì)象名字,如下圖所示 :?
? ? ? ? ? ? ? ? 在this對(duì)象中向下找,可以找到一個(gè)threadLocals屬性,發(fā)現(xiàn)它就是ThreadLocalMap類型,如下圖所示 :?
? ? ? ? ? ? ? ? 我們也可以Thread類的源碼中找到這個(gè)屬性,如下圖所示 :
? ? ? ? ? ? ? ? 而該屬性下的table, 就是實(shí)際存放數(shù)據(jù)的地方(可以看到table是ThreadLocalMap的內(nèi)部類Entry類型的數(shù)組)。當(dāng)set方法執(zhí)行完畢后,我們可以看到table數(shù)組中的Apple對(duì)象,如下圖所示 :
? ? ? ? ? ? ? ? 實(shí)際上,table就是線程用于管理ThreadLocal實(shí)例的容器。
????????????????而table數(shù)組中每個(gè)元素的referent屬性(弱引用對(duì)象),也就是ThreadLocal對(duì)象,此處可以看到與之前一致,如下?:?
? ? ? ? ? ? 2.2 get方法解讀
? ? ? ? ? ? ? ? get方法源碼如下 : (PS : 注意此處是泛型在方法上的應(yīng)用,而不是自定義泛型方法)
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();}
? ? ? ? ? ? ? ? 第一步,和set方法一樣,都是先得到當(dāng)前的線程對(duì)象。為啥?因?yàn)橹挥械玫搅水?dāng)前線程對(duì)象,才能找到它的屬性threadLocals[ThreadLocal$ThreadLocalMap類型],繼而找到該屬性維護(hù)的table數(shù)組(ThreadLocal$ThreadLocalMap$Entry[]類型),然后根據(jù)當(dāng)前線程持有的的ThreadLocal實(shí)例,找到數(shù)組中對(duì)應(yīng)的Entry元素,繼而拿到它的屬性value(保存的數(shù)據(jù))。
? ? ? ? ? ? ? ? 顯然,get方法的源碼中也的確是這么干的。通過(guò)當(dāng)前線程對(duì)象拿到ThreadLocalMap對(duì)象,我們可以看一下getMap(t)的源碼,如下圖所示 :
????????????????之后,判斷map對(duì)象是否為空,如果不為空,就根據(jù)當(dāng)前持有的ThreadLocal實(shí)例去找table數(shù)組中對(duì)應(yīng)的Entry元素。繼續(xù)往下走?:?
? ? ? ? ? ? ? ? 拿到對(duì)應(yīng)的Entry元素后,還要進(jìn)行判斷,如果該元素的確是存在的(表明當(dāng)前的ThreadLocal實(shí)例已經(jīng)為當(dāng)前線程綁定過(guò)數(shù)據(jù)[一個(gè)value]), 就取出該Entry元素的value屬性,此處為Object類型的apple對(duì)象,然后返回。
? ? ? ? ? ? ? ? 🆗,以上就是對(duì)ThreadLocal的一些淺顯解讀。感謝閱讀!