不就是ThreadLocal

最近在重構(gòu)代碼時(shí)淆两,發(fā)現(xiàn)有不同類(lèi)之間參數(shù)的傳遞很復(fù)雜,想到了之前看到的ThreadLocal,于是就想使用ThreadLocal來(lái)解決參數(shù)傳遞的問(wèn)題踢涌,但是在使用之前還是先看了下ThreadLocal的源碼,避免后面出現(xiàn)問(wèn)題序宦。

先簡(jiǎn)單說(shuō)下ThreadLocal的實(shí)現(xiàn)原理睁壁,然后再跟著源碼看下。

每個(gè)ThreadLocal實(shí)例對(duì)應(yīng)一個(gè)當(dāng)前運(yùn)行的Thread線(xiàn)程互捌,每個(gè)Thread線(xiàn)程又有一個(gè)ThreadLocalMap潘明,通過(guò)ThreadLocal類(lèi)的set方法將需要使用的參數(shù)保存在ThreadLocalMap這個(gè)map中,后面只要在同一個(gè)線(xiàn)程中利用ThreadLocal實(shí)例的get方法就可以得到之前設(shè)置的參數(shù)秕噪。
其實(shí)钳降,在看源碼之前,可以簡(jiǎn)單思考下實(shí)現(xiàn)的套路無(wú)非就是:
1. 獲取當(dāng)前線(xiàn)程腌巾。
2. 獲取此線(xiàn)程的ThreadLocalMap
3. 根據(jù)這個(gè)map的key獲取value遂填。

那么,問(wèn)題來(lái)了壤躲,這個(gè)key是啥呢城菊,那就開(kāi)始看下源碼吧。
首先看下ThreaLocal的set方法碉克。

    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

其實(shí)凌唬,從源碼來(lái)看,實(shí)現(xiàn)的套路和我們之前猜想的是一樣的漏麦,主要是看下這個(gè)map的key是啥客税,從map.set(this, value)這句可以看到,這個(gè)map的key就是ThreadLocal實(shí)例的引用撕贞。

然后更耻,看下ThreaLocal的get方法。

    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

get方法的過(guò)程也很簡(jiǎn)單捏膨,根據(jù)threadLocal實(shí)例的引用去獲取ThreadLocalMap中Entry對(duì)象秧均,然后獲取value值食侮。

上面的源碼很簡(jiǎn)單,主要的問(wèn)題是ThreadLocal可能存在內(nèi)存泄露目胡,這也是使用之前想看下源碼的原因锯七,下面就看下為啥會(huì)出現(xiàn)內(nèi)存泄露。
內(nèi)存泄露一定是存儲(chǔ)的數(shù)據(jù)沒(méi)有及時(shí)釋放誉己,導(dǎo)致數(shù)據(jù)占用的內(nèi)存越來(lái)越大眉尸,從上面的源碼來(lái)看,數(shù)據(jù)是存儲(chǔ)在哪里的呢巨双,很明顯是在ThreadLocalMap中噪猾,那放在map中的數(shù)據(jù)為啥占用的內(nèi)存越來(lái)越大呢,那就要看下這個(gè)map的源碼有啥特點(diǎn)了筑累。

    static class ThreadLocalMap {
    
        static class Entry extends WeakReference<ThreadLocal<?>> {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal<?> k, Object v) {
                super(k);
                value = v;
            }
        }

從上面的源碼可以發(fā)現(xiàn)袱蜡,Entry對(duì)象繼承了WeakReference類(lèi),這個(gè)類(lèi)的作用主要就是使某個(gè)對(duì)象的引用為弱引用疼阔,那么弱引用有啥特點(diǎn)呢戒劫,簡(jiǎn)單來(lái)說(shuō),只要觸發(fā)GC婆廊,不管這個(gè)實(shí)例有沒(méi)有被其他對(duì)象引用迅细,都會(huì)被回收。
寫(xiě)到這里淘邻,又想到前段時(shí)間利用WeakHashMap來(lái)簡(jiǎn)單實(shí)現(xiàn)緩存功能茵典,其實(shí)也是利用了WeakHashMap弱引用的特點(diǎn),避免緩存越來(lái)越大宾舅,導(dǎo)致內(nèi)存溢出统阿。
繼續(xù)剛才的分析,可能不太熟悉的同學(xué)這里可能會(huì)有疑問(wèn)筹我,既然弱引用這么容易回收扶平,那么更不可能出現(xiàn)內(nèi)存泄露了。其實(shí)蔬蕊,只是map的key為弱引用结澄,那么key回收后就變?yōu)榱薾ull,但是value還沒(méi)有被回收呢岸夯。假如麻献,我們使用的是線(xiàn)程池,由于線(xiàn)程池中的線(xiàn)程不會(huì)被釋放猜扮,那么這個(gè)線(xiàn)程中對(duì)應(yīng)的ThreadLocalMap也就一直不會(huì)被回收勉吻,如果線(xiàn)程很多的話(huà),那么ThreadLocalMap占用的內(nèi)存就越來(lái)越大旅赢,這樣的話(huà)就可能會(huì)出現(xiàn)內(nèi)存溢出問(wèn)題齿桃。

那么惑惶,如何解決呢,其實(shí)很簡(jiǎn)單源譬,每次使用完ThreadLocal中保存的參數(shù)后集惋,調(diào)用ThreadLocal的remove方法刪除即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末踩娘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子喉祭,更是在濱河造成了極大的恐慌养渴,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泛烙,死亡現(xiàn)場(chǎng)離奇詭異理卑,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)蔽氨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)藐唠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鹉究,你說(shuō)我怎么就攤上這事宇立。” “怎么了自赔?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵妈嘹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我绍妨,道長(zhǎng)润脸,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任他去,我火速辦了婚禮毙驯,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灾测。我一直安慰自己爆价,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布行施。 她就那樣靜靜地躺著允坚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蛾号。 梳的紋絲不亂的頭發(fā)上稠项,一...
    開(kāi)封第一講書(shū)人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音鲜结,去河邊找鬼展运。 笑死活逆,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拗胜。 我是一名探鬼主播蔗候,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼埂软!你這毒婦竟也來(lái)了锈遥?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤勘畔,失蹤者是張志新(化名)和其女友劉穎所灸,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體炫七,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡爬立,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了万哪。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侠驯。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖奕巍,靈堂內(nèi)的尸體忽然破棺而出吟策,到底是詐尸還是另有隱情,我是刑警寧澤伍绳,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布踊挠,位于F島的核電站,受9級(jí)特大地震影響冲杀,放射性物質(zhì)發(fā)生泄漏效床。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一权谁、第九天 我趴在偏房一處隱蔽的房頂上張望剩檀。 院中可真熱鬧,春花似錦旺芽、人聲如沸沪猴。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)运嗜。三九已至,卻和暖如春悯舟,著一層夾襖步出監(jiān)牢的瞬間担租,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工抵怎, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留奋救,地道東北人岭参。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像尝艘,于是被迫代替她去往敵國(guó)和親演侯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354