
作者丨堅持就是勝利
ThreadLocal是什麼
以前面試的時候問到ThreadLocal總是一臉懵逼,隻知道有這個哥們,不了解他是用來做什麼的,更不清楚他的原理了。表面上看他是和多線程,線程同步有關的一個工具類,但其實他與線程同步機制無關。線程同步機制是多個線程共享同一個變量,而ThreadLocal是為每個線程建立一個單獨的變量副本,每個線程都可以改變自己的變量副本而不影響其它線程所對應的副本。
官方API上是這樣介紹的:該類提供了線程局部(thread-local)變量。這些變量不同于它們的普通對應物,因為通路某個變量(通過其 get 或 set 方法)的每個線程都有自己的局部變量,它獨立于變量的初始化副本。ThreadLocal執行個體通常是類中的 private static 字段,它們希望将狀态與某一個線程(例如,使用者 ID 或事務 ID)相關聯。
ThreadLocal定義了四個方法:
get():傳回此線程局部變量目前副本中的值
set(T value):将線程局部變量目前副本中的值設定為指定值
initialValue():傳回此線程局部變量目前副本中的初始值
remove():移除此線程局部變量目前副本中的值
ThreadLocal還有一個特别重要的靜态内部類ThreadLocalMap,該類才是實作線程隔離機制的關鍵。get()、set()、remove()都是基于該内部類進行操作,ThreadLocalMap用鍵值對方式存儲每個線程變量的副本,key為目前的ThreadLocal對象,value為對應線程的變量副本。
試想,每個線程都有自己的ThreadLocal對象,也就是都有自己的ThreadLocalMap,對自己的ThreadLocalMap操作,當然是互不影響的了,這就不存線上程安全問題了,是以ThreadLocal是以空間來交換安全性的解決思路。
假設每個線程都需要一個計數值記錄自己做某件事做了多少次,各線程運作時都需要改變自己的計數值而且互相不影響,那麼ThreadLocal就是很好的選擇,這裡ThreadLocal裡儲存的目前線程的局部變量的副本就是這個計數值。
運作結果:
我們知道SimpleDateFormat在多線程下是存線上程安全問題的,那麼将SimpleDateFormat作為每個線程的局部變量的副本就是每個線程都擁有自己的SimpleDateFormat,就不存線上程安全問題了。
ThreadLocalMap内部是利用Entry來進行key-value的存儲的。
上面源碼中key就是ThreadLocal,value就是值,Entry繼承WeakReference,是以Entry對應key的引用(ThreadLocal執行個體)是一個弱引用。
這個set操作和集合Map解決散列沖突的方法不同,集合Map采用的是鍊位址法,這裡采用的是開放定址法(線性探測)。set()方法中的replaceStaleEntry()和cleanSomeSlots(),這兩個方法可以清除掉key ==null的執行個體,防止記憶體洩漏。
由于采用了開放定址法,目前keu的散列值和元素在數組中的索引并不是一一對應的,首先取一個猜測數(key的散列值),如果所對應的key是我們要找的元素,那麼直接傳回,否則調用getEntryAfterMiss
這裡一直在探測尋找下一個元素,知道找的元素的key是我們要找的。這裡當key==null時,調用expungeStaleEntry有利于GC的回收,用于防止記憶體洩漏。
ThreadLocalMap的key為ThreadLocal執行個體,他是一個弱引用,我們知道弱引用有利于GC的回收,當key == null時,GC就會回收這部分空間,但value不一定能被回收,因為他和Current Thread之間還存在一個強引用的關系。由于這個強引用的關系,會導緻value無法回收,如果線程對象不消除這個強引用的關系,就可能會出現OOM。有些時候,我們調用ThreadLocalMap的remove()方法進行顯式處理。
ThreadLocal不是用來解決共享變量的問題,也不是協調線程同步,他是為了友善各線程管理自己的狀态而引用的一個機制。
每個ThreadLocal内部都有一個ThreadLocalMap,他儲存的key是ThreadLocal的執行個體,他的值是目前線程的局部變量的副本的值。
最近将個人學習筆記整理成冊,使用PDF分享。關注我,回複如下代碼,即可獲得百度盤位址,無套路領取! •001:《Java并發與高并發解決方案》學習筆記;•002:《深入JVM核心——原理、診斷與優化》學習筆記;•003:《Java面試寶典》•004:《Docker開源書》•005:《Kubernetes開源書》•006:《DDD速成(領域驅動設計速成)》•007:全部•008:加技術讨論群
長按/掃碼關注我吧↓↓↓