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