類ThreadLocal的使用及詳解

類ThreadLocal的使用:

? ?變量值的共享可以使用public static變量的形式,所有的線程都使用同一個public static變量膝宁。如果想實現(xiàn)每一個線程都有自己的共享變量效斑,那就可以使用ThreadLocal類造虏。

? ?類ThreadLocal主要解決的就是每個線程綁定自己的值梯醒,可以將ThreadLocal類比喻成全局存放數(shù)據(jù)的盒子,盒子中可以存儲每個線程的私有數(shù)據(jù)郑原。


ThreadLocal詳解:

ThreadLocal定義了四個方法:

get():返回此線程局部變量的當前線程副本中的值贱枣。

initialValue():返回此線程局部變量的當前線程的“初始值”监署。

remove():移除此線程局部變量當前線程的值。

set(T value):將此線程局部變量的當前線程副本中的值設置為指定值纽哥。

除了這四個方法钠乏,ThreadLocal內(nèi)部還有一個靜態(tài)內(nèi)部類ThreadLocalMap,該內(nèi)部類才是實現(xiàn)線程隔離機制的關(guān)鍵春塌,get()晓避、set()、remove()都是基于該內(nèi)部類操作只壳。ThreadLocalMap提供了一種用鍵值對方式存儲每一個線程的變量副本的方法俏拱,key為當前ThreadLocal對象,value則是對應線程的變量副本吼句。

對于ThreadLocal需要注意的有兩點:

ThreadLocal實例本身是不存儲值锅必,它只是提供了一個在當前線程中找到副本值得key。

是ThreadLocal包含在Thread中惕艳,而不是Thread包含在ThreadLocal中搞隐,有些小伙伴會弄錯他們的關(guān)系。


set方法:

set方法

上述代碼可以看出set()方法會先當前線程Thread远搪,之后取出它的成員變量ThreadLocalMap劣纲,如果ThreadLocalMap存在,那么進行KEY/VALUE設置谁鳍,KEY就是ThreadLocal癞季。如果ThreadLocalMap沒有,那么創(chuàng)建一個倘潜。說白了绷柒,當前線程中存在一個Map變量,KEY是ThreadLocal涮因,VALUE是你設置的值辉巡。

可以看出map.set(this,value)中this指代ThreadLocal蕊退,而value就是你設置的值

get方法:


get

這里其實揭示了ThreadLocalMap里面的數(shù)據(jù)存儲結(jié)構(gòu),從上面的代碼來看憔恳,ThreadLocalMap中存放的就是Entry瓤荔,Entry的KEY就是ThreadLocal,VALUE就是值钥组。


private void set(ThreadLocal key, Object value){?

?ThreadLocal.ThreadLocalMap.Entry[] tab = table;

intlen = tab.length;// 根據(jù) ThreadLocal 的散列值输硝,查找對應元素在數(shù)組中的位置

inti = key.threadLocalHashCode & (len-1);// 采用“線性探測法”,尋找合適位置for(ThreadLocal.ThreadLocalMap.Entry e = tab[i];?

?e !=null;?

?e = tab[i = nextIndex(i, len)]) {?

?ThreadLocal k = e.get();// key 存在程梦,直接覆蓋

if(k == key) { e.value = value;return; }// key == null点把,但是存在值(因為此處的e != null)橘荠,說明之前的ThreadLocal對象已經(jīng)被回收了

if(k ==null) {// 用新元素替換陳舊的元素replaceStaleEntry(key, value, i);return;

?}?}// ThreadLocal對應的key實例不存在也沒有陳舊元素,new 一個tab[i] =newThreadLocal.ThreadLocalMap.Entry(key, value);

intsz = ++size;// cleanSomeSlots 清楚陳舊的Entry(key == null)// 如果沒有清理陳舊的 Entry 并且數(shù)組中的元素大于了閾值郎逃,則進行 rehashif(!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }

這個set()操作和我們在集合了解的put()方式有點兒不一樣哥童,雖然他們都是key-value結(jié)構(gòu),不同在于他們解決散列沖突的方式不同褒翰。集合Map的put()采用的是拉鏈法贮懈,而ThreadLocalMap的set()則是采用開放定址法

除此之外set()和get()方法還有一個重要的作用就是清楚key為null的entry。

ThreadLocal的內(nèi)存泄漏問題:

1.ThreadLocal為什么會存在內(nèi)存泄漏問題优训?

再來看一下這個圖


示意圖

ThreadLocalMap使用ThreadLocal的弱引用作為key朵你,如果一個ThreadLocal沒有外部強引用來引用它,那么系統(tǒng) GC 的時候揣非,這個ThreadLocal勢必會被回收抡医,這樣一來,ThreadLocalMap中就會出現(xiàn)key為null的Entry早敬,就沒有辦法訪問這些key為null的Entry的value忌傻,如果當前線程再遲遲不結(jié)束的話,這些key為null的Entry的value就會一直存在一條強引用鏈:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永遠無法回收搁嗓,造成內(nèi)存泄漏芯勘。

其實,ThreadLocalMap的設計中已經(jīng)考慮到這種情況腺逛,也加上了一些防護措施:在ThreadLocal的get(),set(),remove()的時候都會清除線程ThreadLocalMap里所有key為null的value荷愕。

2.那么使用了弱引用后還會存在?

a>使用static的ThreadLocal棍矛,延長了ThreadLocal的生命周期安疗,可能導致的內(nèi)存泄漏

b>分配使用了ThreadLocal又不再調(diào)用get(),set(),remove()方法,那么就會導致內(nèi)存泄漏够委。

3.為什么使用弱引用荐类?

弱引用是對一個對象(稱為referent)的引用的持有者。使用弱引用后茁帽,可以維持對 referent 的引用玉罐,而不會阻止它被垃圾收集。當垃圾收集器跟蹤堆的時候潘拨,如果對一個對象的引用只有弱引用吊输,那么這個 referent 就會成為垃圾收集的候選對象,就像沒有任何剩余的引用一樣铁追,而且所有剩余的弱引用都被清除季蚂。

? ?下面我們分兩種情況討論:

a>key 使用強引用:引用的ThreadLocal的對象被回收了,但是ThreadLocalMap還持有ThreadLocal的強引用,如果沒有手動刪除扭屁,ThreadLocal不會被回收算谈,導致Entry內(nèi)存泄漏。

b>key 使用弱引用:引用的ThreadLocal的對象被回收了料滥,由于ThreadLocalMap持有ThreadLocal的弱引用然眼,即使沒有手動刪除,ThreadLocal也會被回收幔欧。value在下一次ThreadLocalMap調(diào)用set,get罪治,remove的時候會被清除。

比較兩種情況礁蔗,我們可以發(fā)現(xiàn):由于ThreadLocalMap的生命周期跟Thread一樣長觉义,如果都沒有手動刪除對應key,都會導致內(nèi)存泄漏浴井,但是使用弱引用可以多一層保障:弱引用ThreadLocal不會內(nèi)存泄漏晒骇,對應的value在下一次ThreadLocalMap調(diào)用set,get,remove的時候會被清除

因此磺浙,ThreadLocal內(nèi)存泄漏的根源是:由于ThreadLocalMap的生命周期跟Thread一樣長洪囤,如果沒有手動刪除對應key就會導致內(nèi)存泄漏,而不是因為弱引用撕氧。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瘤缩,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子伦泥,更是在濱河造成了極大的恐慌剥啤,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,590評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件不脯,死亡現(xiàn)場離奇詭異府怯,居然都是意外死亡,警方通過查閱死者的電腦和手機防楷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,157評論 3 399
  • 文/潘曉璐 我一進店門牺丙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人复局,你說我怎么就攤上這事冲簿。” “怎么了亿昏?”我有些...
    開封第一講書人閱讀 169,301評論 0 362
  • 文/不壞的土叔 我叫張陵峦剔,是天一觀的道長。 經(jīng)常有香客問我龙优,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,078評論 1 300
  • 正文 為了忘掉前任彤断,我火速辦了婚禮野舶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘宰衙。我一直安慰自己平道,他們只是感情好,可當我...
    茶點故事閱讀 69,082評論 6 398
  • 文/花漫 我一把揭開白布供炼。 她就那樣靜靜地躺著一屋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪袋哼。 梳的紋絲不亂的頭發(fā)上冀墨,一...
    開封第一講書人閱讀 52,682評論 1 312
  • 那天,我揣著相機與錄音涛贯,去河邊找鬼诽嘉。 笑死,一個胖子當著我的面吹牛弟翘,可吹牛的內(nèi)容都是我干的虫腋。 我是一名探鬼主播,決...
    沈念sama閱讀 41,155評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼稀余,長吁一口氣:“原來是場噩夢啊……” “哼悦冀!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起睛琳,我...
    開封第一講書人閱讀 40,098評論 0 277
  • 序言:老撾萬榮一對情侶失蹤盒蟆,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后掸掏,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體茁影,經(jīng)...
    沈念sama閱讀 46,638評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,701評論 3 342
  • 正文 我和宋清朗相戀三年丧凤,在試婚紗的時候發(fā)現(xiàn)自己被綠了募闲。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,852評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡愿待,死狀恐怖浩螺,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仍侥,我是刑警寧澤要出,帶...
    沈念sama閱讀 36,520評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站农渊,受9級特大地震影響患蹂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,181評論 3 335
  • 文/蒙蒙 一传于、第九天 我趴在偏房一處隱蔽的房頂上張望囱挑。 院中可真熱鬧,春花似錦沼溜、人聲如沸平挑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,674評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽通熄。三九已至,卻和暖如春找都,著一層夾襖步出監(jiān)牢的瞬間唇辨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,788評論 1 274
  • 我被黑心中介騙來泰國打工檐嚣, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留助泽,地道東北人。 一個月前我還...
    沈念sama閱讀 49,279評論 3 379
  • 正文 我出身青樓嚎京,卻偏偏與公主長得像嗡贺,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子鞍帝,可洞房花燭夜當晚...
    茶點故事閱讀 45,851評論 2 361

推薦閱讀更多精彩內(nèi)容