天天看點

java并發程式設計學習: ThreadLocal使用及原理

多線程應用中,如果希望一個變量隔離在某個線程内,即:該變量隻能由某個線程本身可見,其它線程無法通路,那麼ThreadLocal可以很友善的幫你做到這一點。 

先來看一下示例:

運作結果:

B:48

A:32

即:線程A與線程B中ThreadLocal儲存的整型變量是各自獨立的,互不相幹,隻要在每個線程内部使用set方法指派,然後線上程内部使用get就能取到對應的值。

把這個示例稍微變化一下:

把ThreadLocal指派的地方放在了MyRunnable的構造函數中,然後在run方法中讀取該值,看下結果:

main:1

main:47

A:null

B:null

思考一下:為什麼會這樣? MyRunnable的構造函數是由main主線程調用的,是以TheadLocal的set方法,實際上是在main主線程的環境中完成的,是以也隻能在main主線程中get到,而run方法運作的上下文是子線程本身,由于run方法中并沒有使用set方法指派,是以get到的是預設空值null.

ThreadLocal還有一個派生的子類:InheritableThreadLocal ,可以允許線程及該線程建立的子線程均可以通路同一個變量(有些OOP中的proteced的意味),這麼解釋可能了解起來比較費勁,還是直接看代碼吧:

R-A => main:1

R-B => main:1

A:1

B:1

觀察下結果,在主線程main中設定了一個InheritableThreadLocal執行個體,并在main主線程中設定了值1,然後main主線程及二個子線程t1,t2均正常get到了該值。 

實作原理:

可以觀察下ThreadLocal及Thread的源碼,大緻了解其實作原理:

ThreadLocal類的get方法

從代碼上看,主要思路如下:

1.取目前線程

2.取得ThreadLocalMap類(先不管這個的實作,從命名上看,了解成一個Map<K,V>容器即可)

3.如果Map容器不為空,則根據ThreadLocal自身的HashCode(見後面的繼續分析)取得對應的Entry(即Map裡的k-v元素對)

4.如果entry不為空,則傳回值

5.如果Map容器為空,則設定初始值

繼續順藤摸瓜:

ThreadLocal的getMap及ThreadLocalMap的getEntry方法

可以發現getMap其實取的是Thread執行個體t上的一個屬性,繼續看Thread的代碼:

說明每個Thread内部都維護着二個ThreadLocalMap,一個應對threadLocals(即:一個Thread内部可以有多個ThreadLocal執行個體),另一個對應着 inheritableThreadLocals,再看ThreadLocal.ThreadLocalMap的getEntry方法

從這裡看,ThreadLocalMap的key是基于ThreadLocal的Hashcode與内部table的長度-1做位運算的整數值,隻要有個印象,threadLocalMap的key跟ThreadLocal執行個體的hashcode有關即可。

最後看看ThreadLocal的setInitialValue方法:

先根據目前線程執行個體t,找到内部維護的ThreadLocalMap容器,如果容器為空,則建立Map執行個體,否則直接把值放進去(Key跟ThreadLocal執行個體本身的hashCode相關)

根據以上分析,對于ThreadLocal的内部實作,其主要思路總結如下:

1. 每個Thread執行個體内部,有二個ThreadLocalMap的K-V容器執行個體(分别對應threadLocals及inheritableThreadLocals), 容器的元素數量,即為Thread執行個體裡的ThreadLocal執行個體個數

2. ThreadLocalMap裡的每個Entry的Key與ThreadLocal執行個體的HashCode相關(這樣,多個ThreadLocal執行個體就不會搞混)

3. 每個ThreadLocal執行個體使用set指派時,實際上是在ThreadLocalMap容器裡,添加(或更新)一條Entry資訊

4. 每個ThreadLocal執行個體使用get取值時,從ThreadLocalMap裡根據key取出value 

參考文章:

<a href="http://qifuguang.me/2015/09/02/%5BJava%E5%B9%B6%E5%8F%91%E5%8C%85%E5%AD%A6%E4%B9%A0%E4%B8%83%5D%E8%A7%A3%E5%AF%86ThreadLocal/" target="_blank">http://qifuguang.me/2015/09/02/[Java并發包學習七]解密ThreadLocal/</a>

<a href="http://ifeve.com/java-threadlocal%E7%9A%84%E4%BD%BF%E7%94%A8/" target="_blank">http://ifeve.com/java-threadlocal%e7%9a%84%e4%bd%bf%e7%94%a8/</a>