ThreadLocal的set方法原理示例解析
前沿知識(shí)
ThreadLocal存儲(chǔ)線程變量,使用set方法設(shè)置變量,使用get方法獲取變量- 線程隔離的實(shí)現(xiàn)是每個(gè)
Thread類(lèi)有一個(gè)類(lèi)型為ThreadLocal.ThreadLocalMap的實(shí)例變量threadLocals。如下圖所示,ThreadLocalMap內(nèi)部有一個(gè)Entry數(shù)組,每個(gè)Entry的key是ThreadLocal,也就是referent對(duì)象,value是設(shè)置的值;該類(lèi)的size變量記錄當(dāng)前數(shù)組使用容量;threshold變量記錄閾值,默認(rèn)總?cè)萘康娜种跏际?0

threadLocal通過(guò)哈希算法決定落于哪一個(gè)Entry,GC時(shí),如果threadLocal沒(méi)有引用,會(huì)被回收,即referent值為null,否則不回收,value不會(huì)回收,因此要使用remove方法刪除對(duì)應(yīng)Entry,否則可能會(huì)出現(xiàn)內(nèi)存泄漏
set方法
ThreadLocal->set():

第一種:如果線程第一次執(zhí)行set方法,此時(shí)map為空,會(huì)創(chuàng)建。在此過(guò)程中初始化entry的個(gè)數(shù)為16,threshold為10,同時(shí)根據(jù)哈希值定位對(duì)應(yīng)下標(biāo)的entry并賦值
如果map不為空,走ThreadLocalMap的set方法,根據(jù)哈希值找到對(duì)應(yīng)的下標(biāo)。從源代碼中可知:
第二種:如果該下標(biāo)為空,那么直接賦值
如果該下標(biāo)不為空,那么從當(dāng)前下標(biāo)開(kāi)始遍歷,直到下一個(gè)entry為null時(shí)停止
第三種:如果entry的key是當(dāng)前thread,直接替換值
第四種:如果循環(huán)結(jié)束,說(shuō)明遇到了空entry,那么直接賦值到該下標(biāo)
如果之前發(fā)生了GC,那么entry不為空,但是key為空,此時(shí)調(diào)用replaceStaleEntry方法
記錄此下標(biāo)為staleSlot、slotToExpunge變量,從當(dāng)前下標(biāo)的前一個(gè)entry開(kāi)始遍歷,直到entry為null時(shí)停止,如果有回收的entry,那么記錄它的下標(biāo),賦值到slotToExpunge變量
從當(dāng)前下標(biāo)的后一個(gè)entry開(kāi)始遍歷,直到entry為null時(shí)停止
第五種:如果遇到了key相等的情況,那么替換值,該entry與staleSlot下標(biāo)的entry交換。如果向前遍歷沒(méi)有找到回收的entry,那么記錄并賦值到slotToExpunge變量。清理過(guò)期entry,最后返回
第六種:如果循環(huán)結(jié)束,說(shuō)明遇到了空entry,也沒(méi)有找到key相等的entry。那么清除staleSlot下標(biāo)的value,然后新建entry。如果有記錄過(guò)期entry,那么會(huì)清理,最后返回
賦值結(jié)束后,還會(huì)進(jìn)行一次嘗試清理,如果沒(méi)有過(guò)期entry,并且當(dāng)前容量大于等于閾值,走擴(kuò)容rehash方法
清理與擴(kuò)容
expungeStaleEntry(staleSlot):由于傳入的下標(biāo)staleSlot所在entry一定是GC之后的,因此會(huì)將entry的值設(shè)為null,隨后刪除entry。從下一個(gè)entry開(kāi)始遍歷,直到entry為null時(shí)停止,如果entry是GC過(guò)的,將value置為null,否則將key重新哈希和分配,這樣的目的是使得entry離正確的下標(biāo)位置更接近一些。最后返回entry為null的坐標(biāo)
cleanSomeSlots(i,n):參數(shù)n一般是當(dāng)前的size值。從i的下一個(gè)entry開(kāi)始遍歷,每遍歷一次,n的值就減少一半,直到為0時(shí)停止。如果所在下標(biāo)的entry是GC過(guò)的,那么會(huì)調(diào)用一次expungeStaleEntry(staleSlot)方法
rehash():首先調(diào)用一次清理方法,然后判斷當(dāng)前容量是否超過(guò)閾值的四分之三(約總?cè)萘康亩种唬?/strong>,然后才真正擴(kuò)容,每次擴(kuò)容一倍。循環(huán)遍歷entry數(shù)組,如果entry發(fā)生GC,那么將值設(shè)置為null,否則將key重新哈希和分配,最后重新計(jì)算閾值和當(dāng)前使用容量
總結(jié)
總的來(lái)說(shuō),執(zhí)行set方法時(shí),一共有六種不同的情況。ThreadLocalMap與HashMap相比,它們的實(shí)現(xiàn)都是數(shù)組+hash定位,但是它們的沖突、擴(kuò)容實(shí)現(xiàn)卻大不相同,ThreadLocalMap還會(huì)清理過(guò)期entry,這種獨(dú)特的實(shí)現(xiàn)方式值得探究
以上就是ThreadLocal的set方法原理示例解析的詳細(xì)內(nèi)容,更多關(guān)于ThreadLocal set方法原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java定時(shí)任務(wù)的三種實(shí)現(xiàn)方法
在應(yīng)用里經(jīng)常都有用到在后臺(tái)跑定時(shí)任務(wù)的需求。舉個(gè)例子,比如需要在服務(wù)后臺(tái)跑一個(gè)定時(shí)任務(wù)來(lái)進(jìn)行垃圾回收2014-04-04
Spring boot學(xué)習(xí)教程之快速入門(mén)篇
這篇文章主要給大家介紹了關(guān)于Spring boot的相關(guān)資料,本文屬于基礎(chǔ)入門(mén)教程,對(duì)各位學(xué)習(xí)Spring boot的新手們具有一定的參考學(xué)習(xí)價(jià)值,,要的朋友們下面來(lái)一起看看吧。2017-04-04
JDK8新特性-java.util.function-Function接口使用
這篇文章主要介紹了JDK8新特性-java.util.function-Function接口使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-04-04

