您好,登錄后才能下訂單哦!
本篇內容主要講解“ThreadLocal的結構是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“ThreadLocal的結構是什么”吧!
在聊 ThreadLocal 前,先做前置知識鋪墊,談談Java對象引用級別。
為了使程序能更靈活地控制對象生命周期,從 JDK1.2 版本開始,JDK把對象的引用級別由高到低分為強引用、軟引用、弱引用、虛引用四種級別。
強引用是我們最常見的對象,它屬于不可回收資源,垃圾回收器(后面簡稱G C)絕對不會回收它,即使是內存不足,J V M寧愿拋出 OutOfMemoryErrorM 異常,使程序終止,也不會來回收強引用對象。
如果對象是軟引用,那它的性質屬于可有可無,因為內存空間充足的情況下,G C不會回收它,但是內存空間緊張,G C發現它僅有軟引用,就會回收該對象,所以軟引用對象適合作為內存敏感的緩存對象。
只有對象僅被 WeakReference 引用,它才是弱引用級別對象,因為對象可以在多處被引用,所以 WeakReference 引用的對象,它可能在其他處被強引用了。
顧名思義,虛引用形同虛設,與其他幾種引用不同,虛引用不會決定對象的生命周期。
如果一個對象僅有虛引用,那它就和沒有任何引用一樣,任何時候都可能被 G C 回收。
ThreadLocal很多地方叫線程本地變量,也有些地方叫線程本地存儲,其實意思差不多。ThreadLocal為變量在每個線程中都創建了一個副本,每個線程可以訪問自己內部的副本變量。
Thread類聲明了成員變量threadLocals,threadLocals才是真正的線程本地變量,因此每個 Thread 都有自己的線程本地變量,所以線程本地變量擁有線程隔離特性,也就是天生的線程安全。
從上圖可以看到 threadLocals 成員變量類是 ThreadLocal.ThreadLocalMap,即是 ThreadLocal 提供的內部類,因此 Thread 線程本地變量的創建、新增、獲取、刪除實現核心,必然是圍繞 threadLocals,所以開發者也是圍繞 threadLocals 實現功能,為了后續重復使用,還會對代碼實現進行封裝復用,而 ThreadLocal 就是線程本地變量工具類,由 J D K 提供,線程本地變量的功能都已經實現好了,開箱即用,造福廣大開發人員。
ThreadLocal常用的方法
set:為當前線程設置變量,當前ThreadLocal作為索引
get:獲取當前線程變量,當前ThreadLocal作為索引
initialValue(鉤子方法需要子類實現):賴加載形式初始化線程本地變量,執行get時,發現線程本地變量為null,就會執行initialValue的內容
remove:清空當前線程的ThreadLocal索引與映射的元素
現在總結出「本地線程變量的作用域,屬于當前線程整個范圍,一個線程可以跨越多個方法使用本地線程變量」,當你希望某些變量在某 Thread 的多個方法中共享 并保證線程安全,那就大膽的使用ThreadLocal(ps:一定要想清楚,是某個變量被Thread生命周期內多個方法共享,還是多個Thread共享這個變量!)。
先來看看User類實現的線程本地變量代碼
通過上圖,相信大伙對 ThreadLocalMap 結構已經非常清晰,不知有沒有細心的小伙伴發現 ThreadLocal 竟被弱引用持有?
為什么ThreadLocal會被弱引用?這塊疑惑后面會給大伙安排的明明白白,最后上一張 ThreadLocalMap 源碼圖。
步驟如下
獲取當前線程
獲取當前線程的本地變量
線程本地變量沒有被創建,執行setInitialValue方法進行初始化,并返回value值
線程本地變量存在,ThreadLocal計算成索引從 本地線程變量 獲取Entry,如果Entry為null,執行setInitialValue方法進行初始化,并返回value值,否則通過Entry獲取value返回
步驟如下
獲取當前線程
獲取線程本地變量
本地變量不為空,當前ThreadLocal為索引設置映射的value,否則創建線程本地變量再做后續的設置操作
為什么 Entry 中對 ThreadLocal 使用弱引用?反問一句,如果使用強引用,會發生什么事情?
我們不知道 key 是什么,如何去獲取映射的value,同樣的道理,都沒有入口去獲取到ThreadContextTest.ThreadLoca,自然沒辦法獲取映射的Entry元素。
設計中采用Map結構存儲數據,卻不能通過key去獲取value,這設計明顯不合理,又因key、value值是強引用,導致 G C 無法回收,造成內存溢出。
所以針對這種不合理的設計場景 J D K 做了優化,對 Entry 中的 ThreadLocal 使用弱引用,當 G C 發現它僅有弱引用的時候,會進行回收。
還沒結束,上面留了個小尾巴,大伙都知道 Entry 中對 ThreadLocal 使用弱引用,但value是強引用,如果出現上面提到的不合理場景,value值無法清理,最終內存溢出。
其實value作為強引用設計屬于合理,如果用軟或弱引用,就出大問題了,程序跑著跑著突然get到了一個null,估計都得罵娘了,所以為解決內存溢出問題 J D K提供remove方法,使開發人員可以選擇手動清理整個Entry元素,防止內存溢出。
還記的之前說過嗎?線程本地變量的生命周期與線程綁定,一般線程的生命周期比較短,線程結束時,線程本地變量自然就銷毀了,軟引用與 remove 會不會有點多余了?
業務瞬息萬變,大部分情況來說線程的生命周期比較短,但也業務場景會導致線程的生命周期較長,甚至可能線程無限循環執行,這些是你沒辦法預料到的,數量一旦上來很容易內存溢出,所以個人建議使用完之后及時清理ThreadLocal,理由如下
生命周期較長的線程場景
無限循環線程的場景
線程池場景(因為線程池可以復用線程,而且公司使用的框架可能會定制化線程池,你不能保證他會在線程池內幫你remove)
到此,相信大家對“ThreadLocal的結構是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。