ThreadLocal詳解

一.對(duì)ThreadLocal的理解

二.深入解析ThreadLocal類

三.ThreadLocal的應(yīng)用場(chǎng)景

參考:
注意原鏈接的評(píng)論部分,作者有錯(cuò)誤的地方
Java并發(fā)編程:深入剖析ThreadLocal

對(duì)ThreadLocal的理解

ThreadLocal,很多地方叫做線程本地變量,也有些地方叫做線程本地存儲(chǔ),其實(shí)意思差不多??赡芎芏嗯笥讯贾繲hreadLocal為變量在每個(gè)線程中都創(chuàng)建了一個(gè)副本(實(shí)際上是將變量保存在Map),那么每個(gè)線程可以訪問自己內(nèi)部的副本變量。
  這句話從字面上看起來很容易理解,但是真正理解并不是那么容易。
  我們還是先來看一個(gè)例子:

Paste_Image.png

假設(shè)有這樣一個(gè)數(shù)據(jù)庫鏈接管理類,這段代碼在單線程中使用是沒有任何問題的,但是如果在多線程中使用呢?很顯然,在多線程中使用會(huì)存在線程安全問題:第一,這里面的2個(gè)方法都沒有進(jìn)行同步,很可能在openConnection方法中會(huì)多次創(chuàng)建connect;第二,由于connect是共享變量,那么必然在調(diào)用connect的地方需要使用到同步來保障線程安全,因?yàn)楹芸赡芤粋€(gè)線程在使用connect進(jìn)行數(shù)據(jù)庫操作,而另外一個(gè)線程調(diào)用closeConnection關(guān)閉鏈接。

所以出于線程安全的考慮,必須將這段代碼的兩個(gè)方法進(jìn)行同步處理,并且在調(diào)用connect的地方需要進(jìn)行同步處理。

這樣將會(huì)大大影響程序執(zhí)行效率,因?yàn)橐粋€(gè)線程在使用connect進(jìn)行數(shù)據(jù)庫操作的時(shí)候,其他線程只有等待。

那么大家來仔細(xì)分析一下這個(gè)問題,這地方到底需不需要將connect變量進(jìn)行共享?事實(shí)上,是不需要的。假如每個(gè)線程中都有一個(gè)connect變量,各個(gè)線程之間對(duì)connect變量的訪問實(shí)際上是沒有依賴關(guān)系的,即一個(gè)線程不需要關(guān)心其他線程是否對(duì)這個(gè)connect進(jìn)行了修改的。

深入解析ThreadLocal類

先了解一下ThreadLocal類提供的幾個(gè)方法:

Paste_Image.png

get()方法是用來獲取ThreadLocal在當(dāng)前線程中保存的變量副本,set()用來設(shè)置當(dāng)前線程中變量的副本,remove()用來移除當(dāng)前線程中變量的副本,initialValue()是一個(gè)protected方法,一般是用來在使用時(shí)進(jìn)行重寫的,它是一個(gè)延遲加載方法,下面會(huì)詳細(xì)說明。

首先我們來看一下ThreadLocal類是如何為每個(gè)線程創(chuàng)建一個(gè)變量的副本的。

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(); }
第一句是取得當(dāng)前線程,然后通過getMap(t)方法獲取到一個(gè)map,map的類型為ThreadLocalMap。然后接著下面獲取到<key,value>鍵值對(duì),注意這里獲取鍵值對(duì)傳進(jìn)去的是 this,而不是當(dāng)前線程t。
  如果獲取成功,則返回value值。
  如果map為空,則調(diào)用setInitialValue方法返回value。
  我們上面的每一句來仔細(xì)分析:
  首先看一下getMap方法中做了什么:
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
可能大家沒有想到的是,在getMap中,是調(diào)用當(dāng)期線程t,返回當(dāng)前線程t中的一個(gè)成員變量threadLocals。
  那么我們繼續(xù)取Thread類中取看一下成員變量threadLocals是什么:
  


  實(shí)際上就是一個(gè)ThreadLocalMap,這個(gè)類型是ThreadLocal類的一個(gè)內(nèi)部類,我們繼續(xù)取看ThreadLocalMap的實(shí)現(xiàn):
  

  可以看到ThreadLocalMap的Entry繼承了WeakReference,并且使用ThreadLocal作為鍵值。
然后再繼續(xù)看setInitialValue方法的具體實(shí)現(xiàn):

  很容易了解,就是如果map不為空,就設(shè)置鍵值對(duì),為空,再創(chuàng)建Map,看一下createMap的實(shí)現(xiàn):
  

  至此,可能大部分朋友已經(jīng)明白了ThreadLocal是如何為每個(gè)線程創(chuàng)建變量的副本的:
  首先,在每個(gè)線程Thread內(nèi)部有一個(gè)ThreadLocal.ThreadLocalMap類型的成員變量threadLocals,這個(gè)threadLocals就是用來存儲(chǔ)實(shí)際的變量副本的,鍵值為當(dāng)前ThreadLocal變量,value為變量副本(即T類型的變量)。
  初始時(shí),在Thread里面,threadLocals為空,當(dāng)通過ThreadLocal變量調(diào)用get()方法或者set()方法,就會(huì)對(duì)Thread類中的threadLocals進(jìn)行初始化,并且以當(dāng)前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。
  然后在當(dāng)前線程里面,如果要使用副本變量,就可以通過get方法在threadLocals里面查找。

ThreadLocal的應(yīng)用場(chǎng)景

最常見的ThreadLocal使用場(chǎng)景為 用來解決 數(shù)據(jù)庫連接、Session管理等。

Paste_Image.png

下面這段代碼摘自:
  http://www.iteye.com/topic/103804

Paste_Image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ThreadLocal在java.lang包中,其主要作用是提供一個(gè)和線程綁定的變量環(huán)境,即通過ThreadLoc...
    charming_coder閱讀 1,369評(píng)論 1 8
  • 1. 概念 ThreadLocal 用于提供線程局部變量,在多線程環(huán)境可以保證各個(gè)線程里的變量獨(dú)立于其它線程里的變...
    zly394閱讀 2,052評(píng)論 0 1
  • ThreadLocal翻譯成中文比較準(zhǔn)確的叫法應(yīng)該是:線程局部變量。 這個(gè)玩意有什么用處,或者說為什么要有這么一個(gè)...
    小小的夢(mèng)想008閱讀 222評(píng)論 0 0
  • 電視劇《我的前半生》的主人公是羅子君,過了十年的富太生活,卻突遭丈夫拋棄,不得不自食其力,與生活抗?fàn)?。大部分人只關(guān)...
    梧桐媽閱讀 687評(píng)論 0 0
  • 抬起平靜的手 止不住亂發(fā)的誓 燕過檐留不下背影 雨整夜攏不起相思 昨日又復(fù)昨日醉 明日豈可待人歸? 轉(zhuǎn)燈漏光的角落...
    沉默的話癆閱讀 272評(píng)論 2 5

友情鏈接更多精彩內(nèi)容