国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

深入了解Threadlocal

qiangdada / 2994人閱讀

摘要:通過(guò)將保存在中,每個(gè)線程都會(huì)擁有屬于自己的,代碼如下所示然后你就可以安心地調(diào)用了,不用考慮線程安全問(wèn)題。這樣設(shè)計(jì)的好處就是,當(dāng)線程死掉之后,沒(méi)有強(qiáng)引用,方便收集器回收。

前言

想必大家都對(duì)Threadlocal很熟悉吧,今天我們就一起來(lái)深入學(xué)習(xí)一下。Threadlocal我更傾向于將其翻譯成線程局部變量。它有什么用處呢?Threadlocal對(duì)象通常用于防止對(duì)可變的單實(shí)例變量或全局變量進(jìn)行共享。在spring中,通過(guò)將事務(wù)上下文保存在靜態(tài)的threadlocal中,當(dāng)框架代碼需要判斷當(dāng)前運(yùn)行的是哪一個(gè)事務(wù)時(shí),只需要從ThreadLocal對(duì)象中獲取事務(wù)上下文,這種機(jī)制很方便,避免了在每個(gè)方法都要傳遞上下文信息。

一個(gè)小例子

眾所周知,SimpleDateFormat不是一個(gè)線程安全的類,在多線程環(huán)境下使用同一個(gè)實(shí)例是不正確的。通過(guò)將SimpleDateFormat保存在Threadlocal中,每個(gè)線程都會(huì)擁有屬于自己的SimpleDateFormat,代碼如下所示:

public class DateFormatUtil {

    public static ThreadLocal dataFormatlocal = new ThreadLocal() {
        @Override
        protected SimpleDateFormat initialValue() {
            return new SimpleDateFormat();
        }
    };

    public static SimpleDateFormat getInstance() {
        return dataFormatlocal.get();
    }

}

然后你就可以安心地調(diào)用了,不用考慮線程安全問(wèn)題。當(dāng)然你也可以在每個(gè)線程內(nèi)部調(diào)用new SimpleDateFormat(),但現(xiàn)實(shí)情況是大部分開(kāi)發(fā)可能并不知道它是線程不安全的,并且將其封裝成一個(gè)單例的工具類。至于如何使用,全憑各位讀者愛(ài)好,這個(gè)例子主要用來(lái)演示Threadloca的基本用法。

腦洞大開(kāi)---自己設(shè)計(jì)個(gè)threadlocal

通過(guò)之前的演示,我們已經(jīng)大致知道threadlocal怎么用,并且清楚threadlocal保證了每個(gè)線程都有一個(gè)局部變量副本。如果以此為需求,讓我們自己去設(shè)計(jì),會(huì)是什么樣子的呢?

static Map threadlocal = new HashMap();

這就是我設(shè)計(jì)的....好吧(╯▽╰),原諒我水平有限....在這個(gè)hashMap中,threadId為key,Object為value,也是可以實(shí)現(xiàn)Threadlocal的。那么我們現(xiàn)在要來(lái)考慮幾個(gè)問(wèn)題。jdk是這樣設(shè)計(jì)的嗎?這樣設(shè)計(jì)很low,但是low在哪了?
答:大師們當(dāng)然不是這樣設(shè)計(jì)了。如果這樣設(shè)計(jì),每個(gè)線程的變量都會(huì)永久的保存在hashMap中,存在內(nèi)存泄漏。

好奇寶寶---jdk是如何實(shí)現(xiàn)threadlocal的

low的寫法我們已經(jīng)見(jiàn)過(guò)了,現(xiàn)在我們一起來(lái)分下jdk是如何設(shè)計(jì)的,本文引用jdk1.8。
讓我們看下threadlocal的結(jié)構(gòu)圖:

類核心方法set、get、initialValue、setInitialValue、remove,后面主要圍繞著這幾個(gè)方法介紹。

類核心變量threadLocalHashCode,nextHashCode,HASH_INCREMENT,其中nextHashCode和HASH_INCREMENT都是靜態(tài)的,所以對(duì)于一個(gè)threadlocal對(duì)象,成員變量只有一個(gè)threadLocalHashCode,這是一個(gè)自定義的hash函數(shù),主要為了減少散列桶的沖突(不是本文重點(diǎn),好奇的可以看下這篇博客https://www.cnblogs.com/ilell...)。

那到底Threadlocal將變量存到哪了呢?眼尖的同學(xué)肯定已經(jīng)發(fā)現(xiàn)了ThreadLocalMap,再來(lái)看下ThreadlocalMap的結(jié)構(gòu):

相信讀過(guò)hashMap源碼同學(xué)的一定會(huì)覺(jué)得非常眼熟,ThreadlocalMap的主體也是Entry[],有resize()和rehash(),區(qū)別僅僅就是在于沒(méi)有使用鏈表結(jié)構(gòu)和紅黑樹(shù)來(lái)處理散列沖突了。Entry的結(jié)構(gòu)我們也很有必要了解一下:

Entry繼承了一個(gè)弱引用,這里簡(jiǎn)單介紹下弱引用的知識(shí)。弱引用是用來(lái)描述非必需對(duì)象的,被弱引用關(guān)聯(lián)的對(duì)象只能生存到下次垃圾收集發(fā)生之前。無(wú)論當(dāng)前內(nèi)存是否足夠,都會(huì)回收掉只被弱引用關(guān)聯(lián)的對(duì)象。很多面試官都喜歡問(wèn)Threadlocal是怎么發(fā)生內(nèi)存泄漏的,其實(shí)就是在問(wèn)這個(gè),這個(gè)需要我們對(duì)Threadlocal有一個(gè)整體的了解才能明白,所以放在最后講。

當(dāng)我們看完Threadlocal的結(jié)構(gòu)了,我們發(fā)現(xiàn)ThreadlocalMap是用來(lái)存儲(chǔ)的數(shù)據(jù)結(jié)構(gòu),那它是怎么和線程關(guān)聯(lián)起來(lái)的呢?這里我們開(kāi)始研究Threadlocal.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);
    }

步驟1:獲取當(dāng)前線程
步驟2:通過(guò)當(dāng)前線程獲取ThreadLocalMap
步驟3:如果map不為null,將當(dāng)前線程和value放到map中,否則創(chuàng)建一個(gè)map。
如何和線程綁定的玄機(jī)就在getMap(t)中。

ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

看到這里,我們就知道了原來(lái)Threadlocal有一個(gè)內(nèi)部類ThreadlocalMap,對(duì)于任何一個(gè)線程都會(huì)有唯一的一個(gè)ThreadlocalMap來(lái)對(duì)應(yīng),而這個(gè)map實(shí)際并不存儲(chǔ)在Threadlocal中,而是存在Thread當(dāng)中,只不過(guò)由Threadlocal暴露了一套api來(lái)維護(hù)Thread的ThreadLocalMap。這樣設(shè)計(jì)的好處就是,當(dāng)線程死掉之后,ThreadLocalMap沒(méi)有強(qiáng)引用,方便收集器回收。

繼續(xù)來(lái)看get()

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

如果前面的流程看懂了,這就很簡(jiǎn)單了。當(dāng)ThreadLocalMap為null或者Entry為null的時(shí)候?qū)?huì)調(diào)用setInitialValue();

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

流程也很簡(jiǎn)單,調(diào)用initialValue()初始化一個(gè)值,獲取當(dāng)前線程的ThreadLocalMap,后面的流程之前都已經(jīng)介紹過(guò)了,這里不再重復(fù),我們主要來(lái)看下initialValue()

protected T initialValue() {
        return null;
    }

請(qǐng)注意,protected修飾,return null;這是一個(gè)初始化值得方法,也就意味著如果業(yè)務(wù)允許的話,需要我們自己實(shí)現(xiàn)initialValue();

ThreadLocal的remove實(shí)現(xiàn)

public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }

ThreadLocalMap的remove實(shí)現(xiàn)

private void remove(ThreadLocal key) {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            for (Entry e = tab[i];
                 e != null;
                 e = tab[i = nextIndex(i, len)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

都非常簡(jiǎn)單,就留給大家自己看了~。~.

Threadlocal容易發(fā)生內(nèi)存泄露?

先回顧一下ThreadlocalMap的結(jié)構(gòu)

static class ThreadLocalMap {

        /**
         * The entries in this hash map extend WeakReference, using
         * its main ref field as the key (which is always a
         * ThreadLocal object).  Note that null keys (i.e. entry.get()
         * == null) mean that the key is no longer referenced, so the
         * entry can be expunged from table.  Such entries are referred to
         * as "stale entries" in the code that follows.
         */
        static class Entry extends WeakReference {
            /** The value associated with this ThreadLocal. */
            Object value;

            Entry(ThreadLocal k, Object v) {
                super(k);
                value = v;
            }
        }
        .....
        .....
}

然后套用網(wǎng)上的一張圖(實(shí)線表示強(qiáng)引用,虛線表示虛引用)

ThreadLocalMap使用ThreadLocal的弱引用作為key,如果一個(gè)ThreadLocal沒(méi)有外部強(qiáng)引用引用他,那么系統(tǒng)gc的時(shí)候,這個(gè)ThreadLocal勢(shì)必會(huì)被回收,這樣一來(lái),ThreadLocalMap中就會(huì)出現(xiàn)key為null的Entry,就沒(méi)有辦法訪問(wèn)這些key為null的Entry的value,如果當(dāng)前線程再遲遲不結(jié)束的話,這些key為null的Entry的value就會(huì)一直存在一條強(qiáng)引用鏈:ThreadLocal Ref -> Thread -> ThreaLocalMap -> Entry -> value永遠(yuǎn)無(wú)法回收,造成內(nèi)存泄露。其實(shí)jdk已經(jīng)考慮到了這種情況,ThreadLocalMap的genEntry函數(shù)或者set函數(shù)會(huì)去遍歷將key為null的給移除掉,但這明顯不是所有情況都成立的,所以需要調(diào)用者自己去調(diào)用remove函數(shù),手動(dòng)刪除掉需要的threadlocal,防止內(nèi)存泄露。然后jdk并不建議在棧內(nèi)聲明threadlocal,而是建議將ThreadLocal變量定義成private static的,這樣的話ThreadLocal的生命周期就更長(zhǎng),由于一直存在ThreadLocal的強(qiáng)引用,所以ThreadLocal也就不會(huì)被回收,也就能保證任何時(shí)候都能根據(jù)ThreadLocal的弱引用訪問(wèn)到Entry的value值,然后remove它,防止內(nèi)存泄露。
放圖證明這是jdk說(shuō)的~~

總結(jié)

再不睡覺(jué),就天明了..

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/69665.html

相關(guān)文章

  • 深入理解Python中的ThreadLocal變量(下)

    摘要:具體怎么實(shí)現(xiàn)的呢,思想其實(shí)特別簡(jiǎn)單,我們?cè)谏钊肜斫庵械淖兞可弦晃牡淖詈笥刑崞疬^(guò),就是創(chuàng)建一個(gè)全局字典,然后將線程或者協(xié)程標(biāo)識(shí)符作為,相應(yīng)線程或協(xié)程的局部數(shù)據(jù)作為。 在上篇我們看到了 ThreadLocal 變量的簡(jiǎn)單使用,中篇對(duì)python中 ThreadLocal 的實(shí)現(xiàn)進(jìn)行了分析,但故事還沒(méi)有結(jié)束。本篇我們一起來(lái)看下Werkzeug中ThreadLocal的設(shè)計(jì)。 Werkzeug...

    dadong 評(píng)論0 收藏0
  • 深入理解Python中的ThreadLocal變量(中)

    摘要:在深入理解中的變量上中我們看到的引入,使得可以很方便地在多線程環(huán)境中使用局部變量。特別需要注意的是,基類的并不會(huì)屏蔽派生類中的創(chuàng)建。到此,整個(gè)源碼核心部分已經(jīng)理解的差不多了,只剩下用來(lái)執(zhí)行清除工作。 在 深入理解Python中的ThreadLocal變量(上) 中我們看到 ThreadLocal 的引入,使得可以很方便地在多線程環(huán)境中使用局部變量。如此美妙的功能到底是怎樣實(shí)現(xiàn)的?如果你...

    DataPipeline 評(píng)論0 收藏0
  • 追蹤解析 ThreadLocal 源碼

    摘要:雖然類名中帶有字樣,但是實(shí)際上并不是接口的子類。是弱連接接口,這意味著如果僅有指向某一類,其任然有可能被回收掉。這里使用弱連接的意義,是為了防止業(yè)務(wù)代碼中置空對(duì)象,但是由于存在連接可達(dá),所以仍然無(wú)法回收掉該對(duì)象的情況發(fā)生。 零 前期準(zhǔn)備 0 FBI WARNING 文章異常啰嗦且繞彎。 1 版本 JDK 版本 : OpenJDK 11.0.1 IDE : idea 2018.3 2 T...

    wawor4827 評(píng)論0 收藏0
  • Java面試題必備知識(shí)之ThreadLocal

    摘要:方法,刪除當(dāng)前線程綁定的這個(gè)副本數(shù)字,這個(gè)值是的值,普通的是使用鏈表來(lái)處理沖突的,但是是使用線性探測(cè)法來(lái)處理沖突的,就是每次增加的步長(zhǎng),根據(jù)參考資料所說(shuō),選擇這個(gè)數(shù)字是為了讓沖突概率最小。 showImg(https://segmentfault.com/img/remote/1460000019828633); 老套路,先列舉下關(guān)于ThreadLocal常見(jiàn)的疑問(wèn),希望可以通過(guò)這篇學(xué)...

    Maxiye 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<