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ù)隔離。