Thread 中 ThreadLocal 源码解读
先了解一下ThreadLocal類(lèi)提供的幾個(gè)方法:
public T get() { } public void set(T value) { } public void remove() { } protected T initialValue() { } get()方法是用來(lái)獲取ThreadLocal在當(dāng)前線程中保存的變量副本,set()用來(lái)設(shè)置當(dāng)前線程中變量的副本,remove()用來(lái)移除當(dāng)前線程中變量的副本,initialValue()是一個(gè)protected方法,一般是用來(lái)在使用時(shí)進(jìn)行重寫(xiě)的,它是一個(gè)延遲加載方法,下面會(huì)詳細(xì)說(shuō)明。首先我們來(lái)看一下ThreadLocal類(lèi)是如何為每個(gè)線程創(chuàng)建一個(gè)變量的副本的。
先看下get方法的實(shí)現(xiàn):
第一句是取得當(dāng)前線程,然后通過(guò)getMap(t)方法獲取到一個(gè)map,map的類(lèi)型為T(mén)hreadLocalMap。然后接著下面獲取到<key,value>鍵值對(duì),注意這里獲取鍵值對(duì)傳進(jìn)去的是? this,而不是當(dāng)前線程t。
如果獲取成功,則返回value值。? ?如果map為空,則調(diào)用setInitialValue方法返回value。
我們上面的每一句來(lái)仔細(xì)分析:
首先看一下getMap方法中做了什么:
可能大家沒(méi)有想到的是,在getMap中,是調(diào)用當(dāng)期線程t,返回當(dāng)前線程t中的一個(gè)成員變量threadLocals。
那么我們繼續(xù)取Thread類(lèi)中取看一下成員變量threadLocals是什么:
實(shí)際上就是一個(gè)ThreadLocalMap,這個(gè)類(lèi)型是ThreadLocal類(lèi)的一個(gè)內(nèi)部類(lè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類(lèi)型的成員變量threadLocals,這個(gè)threadLocals就是用來(lái)存儲(chǔ)實(shí)際的變量副本的,鍵值為當(dāng)前ThreadLocal變量,value為變量副本(即T類(lèi)型的變量)。
初始時(shí),在Thread里面,threadLocals為空,當(dāng)通過(guò)ThreadLocal變量調(diào)用get()方法或者set()方法,就會(huì)對(duì)Thread類(lèi)中的threadLocals進(jìn)行初始化,并且以當(dāng)前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。
測(cè)試輸出的日志:
調(diào)用get方法時(shí),當(dāng)前線程共享變量沒(méi)有設(shè)置,調(diào)用initialValue獲取默認(rèn)值! 線程IntegerTask1: 0 調(diào)用get方法時(shí),當(dāng)前線程共享變量沒(méi)有設(shè)置,調(diào)用initialValue獲取默認(rèn)值! 線程IntegerTask2: 0 調(diào)用get方法時(shí),當(dāng)前線程共享變量沒(méi)有設(shè)置,調(diào)用initialValue獲取默認(rèn)值! 調(diào)用get方法時(shí),當(dāng)前線程共享變量沒(méi)有設(shè)置,調(diào)用initialValue獲取默認(rèn)值! 線程StringTask1: a 線程StringTask2: a 線程StringTask1: aa 線程StringTask2: aa 線程IntegerTask1: 1 線程IntegerTask2: 1 線程StringTask1: aaa 線程StringTask2: aaa 線程IntegerTask2: 2 線程IntegerTask1: 2 線程StringTask2: aaaa 線程StringTask1: aaaa 線程IntegerTask2: 3 線程IntegerTask1: 3 線程StringTask1: aaaaa 線程StringTask2: aaaaa 調(diào)用get方法時(shí),當(dāng)前線程共享變量沒(méi)有設(shè)置,調(diào)用initialValue獲取默認(rèn)值! 線程IntegerTask2: 0 調(diào)用get方法時(shí),當(dāng)前線程共享變量沒(méi)有設(shè)置,調(diào)用initialValue獲取默認(rèn)值! 線程IntegerTask1: 0從測(cè)試輸出的日志可以看出:
ThreadLocalMap 是每一個(gè)線程內(nèi)部自帶的容器,用來(lái)存儲(chǔ)共享對(duì)象的,其map的key是Threadlocal對(duì)象。ThreadLocal new出來(lái)一個(gè)對(duì)象的時(shí)候,同時(shí)又調(diào)用set 設(shè)置共享對(duì)象的時(shí)候,那么ThreadlocalMap 會(huì)在set 方法中創(chuàng)建和初始化。然后將當(dāng)前的threadLocal 作為key,threadLocal 中要set的共享對(duì)象作為value存儲(chǔ)到ThreadlocalMap 中。
可見(jiàn)ThreadlocalMap 存儲(chǔ)的共享對(duì)象只是在set的時(shí)候保證數(shù)據(jù)對(duì)象是一致的,因?yàn)閠hreadLocal 設(shè)置的共享是同一個(gè)對(duì)象。
threadLocal 說(shuō)白了就是對(duì)象的共享,想想我們有很對(duì)的實(shí)現(xiàn)方式,比如static,比如單例的gets,sets。
最常見(jiàn)的ThreadLocal使用場(chǎng)景為 用來(lái)解決 數(shù)據(jù)庫(kù)連接、Session管理等。
總結(jié)
以上是生活随笔為你收集整理的Thread 中 ThreadLocal 源码解读的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 时珍病之是什么意思
- 下一篇: Thread Join 讲解