Java-ThreadLocal詳解

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)系作者
  • 序言:七十年代末局雄,一起剝皮案震驚了整個濱河市甥啄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炬搭,老刑警劉巖蜈漓,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異宫盔,居然都是意外死亡融虽,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進店門灼芭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來有额,“玉大人,你說我怎么就攤上這事彼绷∥∮樱” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵寄悯,是天一觀的道長萤衰。 經(jīng)常有香客問我,道長猜旬,這世上最難降的妖魔是什么脆栋? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任倦卖,我火速辦了婚禮,結(jié)果婚禮上椿争,老公的妹妹穿的比我還像新娘怕膛。我一直安慰自己,他們只是感情好秦踪,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布嘉竟。 她就那樣靜靜地躺著,像睡著了一般洋侨。 火紅的嫁衣襯著肌膚如雪舍扰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天希坚,我揣著相機與錄音边苹,去河邊找鬼。 笑死裁僧,一個胖子當(dāng)著我的面吹牛个束,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播聊疲,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼茬底,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了获洲?” 一聲冷哼從身側(cè)響起阱表,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎贡珊,沒想到半個月后最爬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡门岔,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年爱致,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片寒随。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡糠悯,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出妻往,到底是詐尸還是另有隱情互艾,我是刑警寧澤,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布蒲讯,位于F島的核電站忘朝,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏判帮。R本人自食惡果不足惜局嘁,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一溉箕、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧悦昵,春花似錦肴茄、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至棋凳,卻和暖如春拦坠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背剩岳。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工贞滨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人拍棕。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓晓铆,卻偏偏與公主長得像,于是被迫代替她去往敵國和親绰播。 傳聞我的和親對象是個殘疾皇子骄噪,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容