ThreadLocal

ThreadLocal,很多地方叫做線程本地變量,也有些地方叫做線程本地存儲。ThreadLocal為變量在每個線程中都創(chuàng)建了一個副本,那么每個線程可以訪問自己內(nèi)部的副本變量。

假如每個線程中都有一個connect變量,各個線程之間對connect變量的訪問實(shí)際上是沒有依賴關(guān)系的,即一個線程不需要關(guān)心其他線程是否對這個connect進(jìn)行了修改的。ThreadLocal在每個線程中對該變量會創(chuàng)建一個副本,即每個線程內(nèi)部都會有一個該變量,且在線程內(nèi)部任何地方都可以使用,線程之間互不影響,這樣一來就不存在線程安全問題,也不會嚴(yán)重影響程序執(zhí)行性能。

  但是要注意,雖然ThreadLocal能夠解決上面說的問題,但是由于在每個線程中都創(chuàng)建了副本,所以要考慮它對資源的消耗,比如內(nèi)存的占用會比不使用ThreadLocal要大。



第一句是取得當(dāng)前線程,然后通過getMap(t)方法獲取到一個map,map的類型為ThreadLocalMap。然后接著下面獲取到<key,value>鍵值對,注意這里獲取鍵值對傳進(jìn)去的是? this,而不是當(dāng)前線程t。

如果獲取成功,則返回value值。

如果map為空,則調(diào)用setInitialValue方法返回value。

ThreadLocalMap getMap(Thread t){

? ? return t.threadLocals;

}

在getMap中,是調(diào)用當(dāng)期線程t,返回當(dāng)前線程t中的一個成員變量threadLocals。

成員變量threadLocals是什么:

ThreadLocal.ThreadLocalMap threadLocals = null;

實(shí)際上就是一個ThreadLocalMap,這個類型是ThreadLocal類的一個內(nèi)部類

ThreadLocalMap的Entry繼承了WeakReference,并且使用ThreadLocal作為鍵值。


setInitialValue方法的具體實(shí)現(xiàn):

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


ThreadLocal是如何為每個線程創(chuàng)建變量的副本的:

  首先,在每個線程Thread內(nèi)部有一個ThreadLocal.ThreadLocalMap類型的成員變量threadLocals,這個threadLocals就是用來存儲實(shí)際的變量副本的,鍵值為當(dāng)前ThreadLocal變量,value為變量副本(即T類型的變量)。

  初始時,在Thread里面,threadLocals為空,當(dāng)通過ThreadLocal變量調(diào)用get()方法或者set()方法,就會對Thread類中的threadLocals進(jìn)行初始化,并且以當(dāng)前ThreadLocal變量為鍵值,以ThreadLocal要保存的副本變量為value,存到threadLocals。

  然后在當(dāng)前線程里面,如果要使用副本變量,就可以通過get方法在threadLocals里面查找。

  1)實(shí)際的通過ThreadLocal創(chuàng)建的副本是存儲在每個線程自己的threadLocals中的;

  2)為何threadLocals的類型ThreadLocalMap的鍵值為ThreadLocal對象,因?yàn)槊總€線程中可有多個threadLocal變量,就像上面代碼中的longLocal和stringLocal;

  3)在進(jìn)行g(shù)et之前,必須先set,否則會報空指針異常;

如果想在get之前不需要調(diào)用set就能正常訪問的話,必須重寫initialValue()方法。

如果沒有先set的話,即在map中查找不到對應(yīng)的存儲,則會通過調(diào)用setInitialValue方法返回i,而在setInitialValue方法中,有一個語句是T value = initialValue(), 而默認(rèn)情況下,initialValue方法返回的是null。

---------------------

作者:wespten

來源:CSDN

原文:https://blog.csdn.net/qq_35029061/article/details/86495625

版權(quán)聲明:本文為博主原創(chuàng)文章,轉(zhuǎn)載請附上博文鏈接!


ThreadLocal的內(nèi)部方法

1.protected T initialValue()

①該方法實(shí)現(xiàn)只返回 null,并且修飾符為protected,很明顯,如果用戶想返回初始值不為null,則需要定義線程變量時重寫該方法;

1? ? /**

2? ? ? * @return the initial value for this thread-local

3? ? ? */

4? ? protected T initialValue() {

5? ? ? ? return null;

6? ? }

復(fù)制代碼

1 //定義ThreadLocal時重寫initialValue方法,返回用戶想要的值

2 private static ThreadLocal t = new ThreadLocal() {

3? ? public Object initialValue() {

4? ? ? ? A a = new A();

5? ? ? ? return a;

6? ? }

7 };

復(fù)制代碼

②返回此線程局部變量的當(dāng)前線程的初始值。最多在每次訪問線程來獲得每個線程局部變量時調(diào)用此方法一次,即線程第一次使用 get() 方法訪問變量的時候。如果線程先于 get 方法調(diào)用 set(T) 方法,則不會在線程中再調(diào)用 initialValue 方法。

復(fù)制代碼

1? ? /**

2? ? ? * Returns the value in the current thread's copy of this

3? ? ? * thread-local variable.? If the variable has no value for the

4? ? ? * current thread, it is first initialized to the value returned

5? ? ? * by an invocation of the {@link #initialValue} method.

6? ? ? *

7? ? ? * @return the current thread's value of this thread-local

8? ? ? */

9? ? public T get() {

10? ? ? ? Thread t = Thread.currentThread();

11? ? ? ? ThreadLocalMap map = getMap(t);

12? ? ? ? if (map != null) {

13? ? ? ? ? ? ThreadLocalMap.Entry e = map.getEntry(this);

14? ? ? ? ? ? if (e != null)

15? ? ? ? ? ? ? ? return (T)e.value;

16? ? ? ? }

17? ? ? ? return setInitialValue();

18? ? }? ?

復(fù)制代碼

2.public T get()

①在1中已經(jīng)提到,該方法返回當(dāng)前線程變量副本。如果這是線程第一次調(diào)用該方法,則創(chuàng)建并初始化此副本。

3.public void set(T value)

①ThreadLocal中有個內(nèi)部靜態(tài)類ThreadLocalMap,用來存放當(dāng)前線程變量副本中的值,鍵為當(dāng)前線程變量對象,值為用戶設(shè)的值;

②當(dāng)使用ThreadLocal存值時,首先是獲取到當(dāng)前線程對象,然后獲取到當(dāng)前線程本地變量Map,最后將當(dāng)前使用的ThreadLocal和傳入的值放到Map中,也就是說ThreadLocalMap中存的值是[ThreadLocal對象, 存放的值],這樣做的好處是,每個線程都對應(yīng)一個本地變量的Map,所以一個線程可以存在多個線程本地變量(即不同的ThreadLocal,就如1中所說,可以重寫initialValue,返回不同類型的子類)。

復(fù)制代碼

1? ? /**

2? ? ? * Sets the current thread's copy of this thread-local variable

3? ? ? * to the specified value.? Most subclasses will have no need to

4? ? ? * override this method, relying solely on the {@link #initialValue}

5? ? ? * method to set the values of thread-locals.

6? ? ? *

7? ? ? * @param value the value to be stored in the current thread's copy of

8? ? ? *? ? ? ? this thread-local.

9? ? ? */

10? ? public void set(T value) {

11? ? ? ? Thread t = Thread.currentThread();

12? ? ? ? ThreadLocalMap map = getMap(t);

13? ? ? ? if (map != null)

14? ? ? ? ? ? map.set(this, value);

15? ? ? ? else

16? ? ? ? ? ? createMap(t, value);

17? ? }

復(fù)制代碼

4.public void remove()

①移除此線程中某個ThreadLocal的值,目的是為了減少內(nèi)存的占用。如果再次訪問此線程局部變量,那么在默認(rèn)情況下它將擁有其initialValue;

②只有從jdk1.5開始才有該方法;

③當(dāng)線程結(jié)束后,對應(yīng)該線程的局部變量將自動被垃圾回收,因此顯式調(diào)用remove清除線程的局部變量并不是必須的操作,但它可以加快內(nèi)存回收的速度。

復(fù)制代碼

1? ? ? /**

2? ? ? * Removes the current thread's value for this thread-local

3? ? ? * variable.? If this thread-local variable is subsequently

4? ? ? * {@linkplain #get read} by the current thread, its value will be

5? ? ? * reinitialized by invoking its {@link #initialValue} method,

6? ? ? * unless its value is {@linkplain #set set} by the current thread

7? ? ? * in the interim.? This may result in multiple invocations of the

8? ? ? * <tt>initialValue</tt> method in the current thread.

9? ? ? *

10? ? ? * @since 1.5

11? ? ? */

12? ? ? public void remove() {

13? ? ? ? ? ThreadLocalMap m = getMap(Thread.currentThread());

14? ? ? ? ? if (m != null)

15? ? ? ? ? ? ? m.remove(this);

16? ? ? }

復(fù)制代碼

三、ThreadLocal和同步機(jī)制synchonzied相比

1.synchonzied同步機(jī)制是為了實(shí)現(xiàn)同步多線程對相同資源的并發(fā)訪問控制。同步的主要目的是保證多線程間的數(shù)據(jù)共享。同步會帶來巨大的性能開銷,所以同步操作應(yīng)該是細(xì)粒度的(對象中的不同元素使用不同的鎖,而不是整個對象一個鎖)。如果同步使用得當(dāng),帶來的性能開銷是微不足道的。使用同步真正的風(fēng)險是復(fù)雜性和可能破壞資源安全,而不是性能。

2.ThreadLocal以空間換取時間,提供了一種非常簡便的多線程實(shí)現(xiàn)方式。因?yàn)槎鄠€線程并發(fā)訪問無需進(jìn)行等待,所以使用ThreadLocal會獲得更大的性能。

3.ThreadLocal中的對象,通常都是比較小的對象。另外使用ThreadLocal不能使用原子類型,只能使用Object類型。ThreadLocal的使用比synchronized要簡單得多。

4.synchronized是利用鎖的機(jī)制,使變量或代碼塊在某一時該只能被一個線程訪問。而ThreadLocal為每一個線程都提供了變量的副本,使得每個線程在某一時間訪問到的并不是同一個對象,這樣就隔離了多個線程對數(shù)據(jù)的數(shù)據(jù)共享。而Synchronized卻正好相反,它用于在多個線程間通信時能夠獲得數(shù)據(jù)共享。

5.Synchronized用于線程間的數(shù)據(jù)共享,而ThreadLocal則用于線程間的數(shù)據(jù)隔離。

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

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

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