摘要:并沒(méi)有提供語(yǔ)言級(jí)的線程局部變量,而是在類庫(kù)里提供了線程局部變量的功能,也就是這次的主角類。
Yuicon 轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處,謝謝!序
在多線程環(huán)境下,訪問(wèn)非線程安全的變量時(shí)必須進(jìn)行線程同步,例如使用synchronized方式訪問(wèn)HashMap實(shí)例。但是同步訪問(wèn)會(huì)降低并發(fā)性,影響系統(tǒng)性能。這時(shí)候就可以用空間換時(shí)間,如果我們給每個(gè)線程都分配一個(gè)獨(dú)立的變量,就可以用非同步的方式使用非線程安全的變量,我們稱這種變量為線程局部變量。
顧名思義,線程局部變量是指每個(gè)線程都有一份屬于自己獨(dú)立的變量副本,不會(huì)像普通局部變量一樣可以被其他線程訪問(wèn)到。Java并沒(méi)有提供語(yǔ)言級(jí)的線程局部變量,而是在類庫(kù)里提供了線程局部變量的功能,也就是這次的主角ThreadLocal類。
ThreadLocal的使用Java8版本的ThreadLocal有上圖所示的4個(gè)public方法和一個(gè)protected的方法,第一個(gè)方法用于返回初始值,默認(rèn)是null。第二個(gè)靜態(tài)方法withInitial(Supplier extends S> supplier)是Java8版本新添加的,后面三個(gè)實(shí)例方法則非常的簡(jiǎn)單。
在Java8之前,使用ThreadLocal時(shí)想要設(shè)置初始值時(shí)需要繼承ThreadLocal類覆蓋protected T initialValue()方法才行,例如:
ThreadLocalthreadLocal = new ThreadLocal () { @Override protected Integer initialValue() { return 0; } };
在Java8版本可以使用新添加的靜態(tài)方法withInitial(Supplier extends S> supplier),非常方便的設(shè)置初始值,例如:
ThreadLocalThreadLocal的原理threadLocal = ThreadLocal.withInitial(() -> 0); System.out.println(threadLocal.get()); threadLocal.set(16); System.out.println(threadLocal.get()); threadLocal.remove(); System.out.println(threadLocal.get()); // 同一個(gè)線程的輸出 0 16 0 Process finished with exit code 0
那么ThreadLocal是怎么實(shí)現(xiàn)線程局部變量的功能的呢?其實(shí)ThreadLocal的基本原理并沒(méi)有十分復(fù)雜。ThreadLocal在內(nèi)部定義了一個(gè)靜態(tài)類ThreadLocalMap,ThreadLocalMap的鍵為ThreadLocal對(duì)象,ThreadLocalMap的值就是ThreadLocal存儲(chǔ)的值,不過(guò)這個(gè)ThreadLocalMap是在Thread類里維護(hù)的。我們來(lái)看一下ThreadLocal的部分源碼:
// ThreadLocal的set方法 public void set(T value) { // 獲取當(dāng)前線程對(duì)象 Thread t = Thread.currentThread(); // 獲取Map ThreadLocalMap map = getMap(t); if (map != null) // 設(shè)置值 map.set(this, value); else // 初始化Map createMap(t, value); } // ThreadLocal的createMap方法 void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); } // Thread類定義的實(shí)例域 /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
可以看出ThreadLocal的核心實(shí)現(xiàn)就是ThreadLocalMap的實(shí)現(xiàn)了,ThreadLocalMap內(nèi)部聲明了一個(gè)Entry類來(lái)存儲(chǔ)數(shù)據(jù):
static class Entry extends WeakReference> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal> k, Object v) { super(k); value = v; } }
ThreadLocalMap的實(shí)現(xiàn)與HashMap的實(shí)現(xiàn)有相似的地方,比如同樣是使用數(shù)組存儲(chǔ)數(shù)據(jù)和自動(dòng)擴(kuò)容,不同的是hash算法與hash碰撞后的處理不一樣。
// ThreadLocalMap的set方法 private void set(ThreadLocal> key, Object value) { Entry[] tab = table; int len = tab.length; // 計(jì)算在Entry[]中的索引,每個(gè)ThreadLocal對(duì)象都有一個(gè)hash值threadLocalHashCode,每初始化一個(gè)ThreadLocal對(duì)象,hash值就增加一個(gè)固定的大小0x61c88647 int i = key.threadLocalHashCode & (len-1); for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal> k = e.get(); // 如果鍵已存在就更新值 if (k == key) { e.value = value; return; } // 代替無(wú)效的鍵 if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); }
可以看到ThreadLocalMap把Entry[]數(shù)組當(dāng)成一個(gè)圓環(huán)。從計(jì)算出來(lái)的索引位置開(kāi)始,如果該索引已經(jīng)有數(shù)據(jù)了就判斷Key是否相同,相同就更新值。否則就直到找到一個(gè)空的位置把值放進(jìn)去。獲取值的時(shí)候也類似,從計(jì)算出來(lái)的索引位置開(kāi)始一個(gè)一個(gè)檢查Key是否相同,這樣hash碰撞比較多的話可能性能就不是很好。
ThreadLocal的應(yīng)用ThreadLocal的應(yīng)用是非常廣的,比如Java工程師非常熟悉的Spring框架中就使用了ThreadLocal來(lái)把非線程安全的狀態(tài)性對(duì)象封裝起來(lái),所以我們可以把絕大部分的Bean聲明為singleton作用域。我們?cè)诰帉?xiě)多線程代碼時(shí)也可以想想是用同步的方式訪問(wèn)非線程安全的狀態(tài)性對(duì)象比較好,還是使用ThreadLocal把非線程安全的狀態(tài)性對(duì)象封裝起來(lái)更好。
后記本來(lái)下定決心準(zhǔn)備一周一篇的,結(jié)果偷懶了一次后趕上了公司旅游。這一下子摸了兩篇,只能后面慢慢補(bǔ)了……ThreadLocal我很早就看到過(guò)了,一直沒(méi)什么實(shí)感,直到在《精通Spring 4.X 企業(yè)應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)》看到在Spring中的應(yīng)用后才發(fā)現(xiàn),我從來(lái)沒(méi)想過(guò)為什么Spring里的Dao類可以聲明為單例作用域……沒(méi)有舉一反三的能力就只能多看書(shū)了,活到老學(xué)到老。
參考資料:《Java核心技術(shù) 卷一》
《精通Spring 4.X 企業(yè)應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)》
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/71698.html
摘要:基本在項(xiàng)目開(kāi)發(fā)中基本不會(huì)用到但是面試官是比較喜歡問(wèn)這類問(wèn)題的所以還是有必要了解一下該類的功能與原理的是什么是一個(gè)將在多線程中為每一個(gè)線程創(chuàng)建單獨(dú)的變量副本的類當(dāng)使用來(lái)維護(hù)變量時(shí)會(huì)為每個(gè)線程創(chuàng)建單獨(dú)的變量副本避免因多線程操作共享變量而導(dǎo)致的數(shù) ThreadLocal基本在項(xiàng)目開(kāi)發(fā)中基本不會(huì)用到, 但是面試官是比較喜歡問(wèn)這類問(wèn)題的;所以還是有必要了解一下該類的功能與原理的. Thread...
摘要:案例中的類就是線程獨(dú)有對(duì)象的代理者參與者參與者會(huì)處理多個(gè)委托的工作。然而,的實(shí)現(xiàn)思路讓每個(gè)對(duì)象,自身持有一個(gè),這個(gè)的就是當(dāng)前對(duì)象,是本地線程變量值。 一、定義 Thread-Specific Storage就是線程獨(dú)有的存儲(chǔ)庫(kù),該模式會(huì)對(duì)每個(gè)線程提供獨(dú)有的內(nèi)存空間。java.lang.ThreadLocal類提供了該模式的實(shí)現(xiàn),ThreadLocal的實(shí)例是一種集合(collecti...
摘要:本人郵箱歡迎轉(zhuǎn)載轉(zhuǎn)載請(qǐng)注明網(wǎng)址代碼已經(jīng)全部托管有需要的同學(xué)自行下載引言之前我們講到都是多線程共享數(shù)據(jù)那么有沒(méi)有某一個(gè)共享的變量在這變量里面每個(gè)線程都能擁有自己的屬性呢比如說(shuō)去旅店開(kāi)房休息那么這個(gè)旅店就是一個(gè)共享的數(shù)據(jù)但是每個(gè)人開(kāi)的房間是不一 本人郵箱: 歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明網(wǎng)址 http://blog.csdn.net/tianshi_kcogithub: https://github...
摘要:方法,刪除當(dāng)前線程綁定的這個(gè)副本數(shù)字,這個(gè)值是的值,普通的是使用鏈表來(lái)處理沖突的,但是是使用線性探測(cè)法來(lái)處理沖突的,就是每次增加的步長(zhǎng),根據(jù)參考資料所說(shuō),選擇這個(gè)數(shù)字是為了讓沖突概率最小。 showImg(https://segmentfault.com/img/remote/1460000019828633); 老套路,先列舉下關(guān)于ThreadLocal常見(jiàn)的疑問(wèn),希望可以通過(guò)這篇學(xué)...
摘要:基本原理線程本地變量是和線程相關(guān)的變量,一個(gè)線程則一份數(shù)據(jù)。其中為聲明的對(duì)象。對(duì)于一個(gè)對(duì)象倘若沒(méi)有成員變量,單例非常簡(jiǎn)單,不用去擔(dān)心多線程同時(shí)對(duì)成員變量修改而產(chǎn)生的線程安全問(wèn)題。并且還不能使用單例模式,因?yàn)槭遣荒芏嗑€程訪問(wèn)的。 ThreadLocal簡(jiǎn)述 下面我們看一下ThreadLocal類的官方注釋。 This class provides thread-local variab...
閱讀 2309·2021-11-12 10:36
閱讀 2099·2021-11-09 09:49
閱讀 2782·2021-11-04 16:12
閱讀 1316·2021-10-09 09:57
閱讀 3383·2019-08-29 17:24
閱讀 2071·2019-08-29 15:12
閱讀 1450·2019-08-29 14:07
閱讀 1439·2019-08-29 12:53