最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

ThreadLocal原理介紹及應(yīng)用場景

 更新時(shí)間:2021年12月25日 09:45:03   作者:pony-zi  
本文詳細(xì)講解了ThreadLocal原理介紹及應(yīng)用場景,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

本次給大家介紹重要的工具ThreadLocal。講解內(nèi)容如下,同時(shí)介紹什么場景下發(fā)生內(nèi)存泄漏,如何復(fù)現(xiàn)內(nèi)存泄漏,如何正確使用它來避免內(nèi)存泄漏。

  • ThreadLocal是什么?有哪些用途?
  • ThreadLocal如何使用
  • ThreadLocal原理
  • ThreadLocal使用有哪些坑及注意事項(xiàng)

1. ThreadLocal是什么?有哪些用途?

首先介紹Thread類中屬性threadLocals:

/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */

ThreadLocal.ThreadLocalMap threadLocals = null;

我們發(fā)現(xiàn)Thread并沒有提供成員變量threadLocals的設(shè)置與訪問的方法,那么每個(gè)線程的實(shí)例threadLocals參數(shù)我們?nèi)绾尾僮髂??這時(shí)我們的主角:ThreadLocal就登場了。

所以有那么一句總結(jié):ThreadLocal是線程Thread中屬性threadLocals的管理者。

也就是說我們對于ThreadLocal的get, set,remove的操作結(jié)果都是針對當(dāng)前線程Thread實(shí)例的threadLocals存,取,刪除操作。類似于一個(gè)開發(fā)者的任務(wù),產(chǎn)品經(jīng)理左右不了,產(chǎn)品經(jīng)理只能通過技術(shù)leader來給開發(fā)者分配任務(wù)。下面再舉個(gè)栗子,進(jìn)一步說明他們之間的關(guān)系:

1.每個(gè)人都一張銀行卡

2.每個(gè)人每張卡都有一定的余額。

3.每個(gè)人獲取銀行卡余額都必須通過該銀行的管理系統(tǒng)。

4.每個(gè)人都只能獲取自己卡持有的余額信息,他人的不可訪問。

映射到我們要說的ThreadLocal

  • 1.card類似于Thread
  • 2.card余額屬性,卡號屬性等類似于Treadlocal內(nèi)部屬性集合threadLocals
  • 3.cardManager類似于ThreadLocal管理類

那ThreadLocal有哪些應(yīng)用場景呢?

其實(shí)我們無意間已經(jīng)時(shí)時(shí)刻刻在使用ThreadLocal提供的便利,如果說多數(shù)據(jù)源的切換你比較陌生,那么spring提供的聲明式事務(wù)就再熟悉不過了,我們在研發(fā)過程中無時(shí)無刻不在使用,而spring聲明式事務(wù)的重要實(shí)現(xiàn)基礎(chǔ)就是ThreadLocal,只不過大家沒有去深入研究spring聲明式事務(wù)的實(shí)現(xiàn)機(jī)制。后面有機(jī)會我會給大家介紹spring聲明式事務(wù)的原理及實(shí)現(xiàn)機(jī)制。

原來ThreadLocal這么強(qiáng)大,但應(yīng)用開發(fā)者使用較少,同時(shí)有些研發(fā)人員對于ThreadLocal內(nèi)存泄漏,等潛在問題,不敢試用,恐怕這是對于ThreadLocal最大的誤解,后面我們將會仔細(xì)分析,只要按照正確使用方式,就沒什么問題。如果ThreadLocal存在問題,豈不是spring聲明式事務(wù)是我們程序最大的潛在危險(xiǎn)嗎?

2.ThreadLocal如何使用

為了更直觀的體會ThreadLocal的使用我們假設(shè)如下場景

  • 1.我們給每個(gè)線程生成一個(gè)ID。
  • 2.一旦設(shè)置,線程生命周期內(nèi)不可變化。
  • 3.容器活動期間不可以生成重復(fù)的ID

我們創(chuàng)建一個(gè)ThreadLocal管理類:

測試程序如下:我們同一個(gè)線程不斷get,測試id是否變化,同時(shí)測試完成后我們就將其釋放掉。

在主程序中我們開啟多個(gè)線程測試不通線程之間是否會影響

不出意外我們的結(jié)果為:

結(jié)果:確實(shí)是不同線程間id不同,相同線程id相同。

3.ThreadLocal原理

①ThreadLocal類結(jié)構(gòu)及方法解析:

上圖可知:ThreadLocal三個(gè)方法get, set , remove以及內(nèi)部類`ThreadLocalMap

②ThreadLocal及Thread之間的關(guān)系:

從這張圖我們可以直觀的看到Thread中屬性threadLocals,作為一個(gè)特殊的Map,它的key值就是我們ThreadLocal實(shí)例,而value值這是我們設(shè)置的值。

③ThreadLocal的操作過程:

我們以get方法為例:

其中g(shù)etMap(t)返回的就上當(dāng)前線程的threadlocals,如下圖,然后根據(jù)當(dāng)前ThreadLocal實(shí)例對象作為key獲取ThreadLocalMap中的value,如果首次進(jìn)來這調(diào)用setInitialValue()

set的過程也類似:

注意:ThreadLocal中可以直接t.threadLocals是因?yàn)?code>Thread與ThreadLocal在同一個(gè)包下,同樣Thread可以直接訪問ThreadLocal.ThreadLocalMap threadLocals = null;來進(jìn)行聲明屬性。

4.ThreadLocal使用有哪些坑及注意事項(xiàng)

我經(jīng)常在網(wǎng)上看到駭人聽聞的標(biāo)題,ThreadLocal導(dǎo)致內(nèi)存泄漏,這通常讓一些剛開始對ThreadLocal理解不透徹的開發(fā)者,不敢貿(mào)然使用。越不用,越陌生。這樣就讓我們錯(cuò)失了更好的實(shí)現(xiàn)方案,所以敢于引入新技術(shù),敢于踩坑,才能不斷進(jìn)步。

我們來看下為什么說ThreadLocal會引起內(nèi)存泄漏,什么場景下會導(dǎo)致內(nèi)存泄漏?

先回顧下什么叫內(nèi)存泄漏,對應(yīng)的什么叫內(nèi)存溢出

  • ①M(fèi)emory overflow:內(nèi)存溢出,沒有足夠的內(nèi)存提供申請者使用。
  • ②Memory leak:內(nèi)存泄漏,程序申請內(nèi)存后,無法釋放已申請的內(nèi)存空間,內(nèi)存泄漏的堆積終將導(dǎo)致內(nèi)存溢出。

顯然是TreadLocal在不規(guī)范使用的情況下導(dǎo)致了內(nèi)存沒有釋放。

紅框里我們看到了一個(gè)特殊的類WeakReference,同樣這個(gè)類,應(yīng)用開發(fā)者也同樣很少使用,這里簡單介紹下吧

類型 回收時(shí)間 應(yīng)用場景
強(qiáng)引用 一直存活,除非GC Roots不可達(dá) 所有程序的場景,基本對象,自定義對象等
軟引用 內(nèi)存不足時(shí)會被回收 一般用在對內(nèi)存非常敏感的資源上,用作緩存的場景比較多,例如:網(wǎng)頁緩存、圖片緩存
弱引用 只能存活到下一次GC前 生命周期很短的對象,例如ThreadLocal中的Key。
虛引用 隨時(shí)會被回收, 創(chuàng)建了可能很快就會被回收 可能被JVM團(tuán)隊(duì)內(nèi)部用來跟蹤JVM的垃圾回收活動

既然WeakReference在下一次gc即將被回收,那么我們的程序?yàn)槭裁礇]有出問題呢?

  • ①所以我們測試下弱引用的回收機(jī)制:

這一種存在強(qiáng)引用不會被回收。

這里沒有強(qiáng)引用將會被回收。

上面演示了弱引用的回收情況,下面我們看下ThreadLocal的弱引用回收情況。

  • ThreadLocal的弱引用回收情況

如上圖所示,我們在作為key的ThreadLocal對象沒有外部強(qiáng)引用,下一次gc必將產(chǎn)生key值為null的數(shù)據(jù),若線程沒有及時(shí)結(jié)束必然出現(xiàn),一條強(qiáng)引用鏈

Threadref–>Thread–>ThreadLocalMap–>Entry,所以這將導(dǎo)致內(nèi)存泄漏。

下面我們模擬復(fù)現(xiàn)ThreadLocal導(dǎo)致內(nèi)存泄漏:

1.為了效果更佳明顯我們將我們的treadlocals的存儲值value設(shè)置為1萬字符串的列表:

class ThreadLocalMemory {
    // Thread local variable containing each thread's ID
    public ThreadLocal<List<Object>> threadId = new ThreadLocal<List<Object>>() {
        @Override
        protected List<Object> initialValue() {
            List<Object> list = new ArrayList<Object>();
            for (int i = 0; i < 10000; i++) {
                list.add(String.valueOf(i));
            }
            return list;
        }
    };
    // Returns the current thread's unique ID, assigning it if necessary
    public List<Object> get() {
        return threadId.get();
    }
    // remove currentid
    public void remove() {
        threadId.remove();
    }
}

測試代碼如下:

public static void main(String[] args)
            throws InterruptedException {

        //  為了復(fù)現(xiàn)key被回收的場景,我們使用臨時(shí)變量
        ThreadLocalMemory memeory = new ThreadLocalMemory();

        // 調(diào)用
        incrementSameThreadId(memeory);

        System.out.println("GC前:key:" + memeory.threadId);
        System.out.println("GC前:value-size:" + refelectThreadLocals(Thread.currentThread()));

        // 設(shè)置為null,調(diào)用gc并不一定觸發(fā)垃圾回收,但是可以通過java提供的一些工具進(jìn)行手工觸發(fā)gc回收。
        memeory.threadId = null;
        System.gc();

        System.out.println("GC后:key:" + memeory.threadId);
        System.out.println("GC后:value-size:" + refelectThreadLocals(Thread.currentThread()));

        // 模擬線程一直運(yùn)行
        while (true) {
        }
    }

此時(shí)我們?nèi)绾沃纼?nèi)存中存在memory leak呢?

我們可以借助jdk提供的一些命令dump當(dāng)前堆內(nèi)存,命令如下:

jmap -dump:live,format=b,file=heap.bin <pid>

然后我們借助MAT可視化分析工具,來查看對內(nèi)存,分析對象實(shí)例的存活狀態(tài):

首先打開我們工具提示我們的內(nèi)存泄漏分析:

這里我們可以確定的是ThreadLocalMap實(shí)例的Entry.value是沒有被回收的。

最后我們要確定Entry.key是否還在?打開Dominator Tree,搜索我們的ThreadLocalMemory,發(fā)現(xiàn)并沒有存活的實(shí)例。

以上我們復(fù)現(xiàn)了ThreadLocal不正當(dāng)使用,引起的內(nèi)存泄漏。demo在這里。

所以我們總結(jié)了使用ThreadLocal時(shí)會發(fā)生內(nèi)存泄漏的前提條件:

  • ThreadLocal引用被設(shè)置為null,且后面沒有set,get,remove操作。
  • ②線程一直運(yùn)行,不停止。(線程池)
  • ③觸發(fā)了垃圾回收。(Minor GC或Full GC)

我們看到ThreadLocal出現(xiàn)內(nèi)存泄漏條件還是很苛刻的,所以我們只要破壞其中一個(gè)條件就可以避免內(nèi)存泄漏,單但為了更好的避免這種情況的發(fā)生我們使用ThreadLocal時(shí)遵守以下兩個(gè)小原則:

  • ①ThreadLocal申明為private static final。
    Private與final 盡可能不讓他人修改變更引用,
    Static 表示為類屬性,只有在程序結(jié)束才會被回收。
  • ②ThreadLocal使用后務(wù)必調(diào)用remove方法。
    最簡單有效的方法是使用后將其移除。

到此這篇關(guān)于ThreadLocal原理介紹及應(yīng)用場景的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java?如何獲取某年的第一天和最后一天

    Java?如何獲取某年的第一天和最后一天

    在統(tǒng)計(jì)的數(shù)據(jù)是時(shí)候,要統(tǒng)計(jì)某年的數(shù)據(jù),開始時(shí)間是某年的第一天,結(jié)束時(shí)間是某年的最后一天,該如何獲取某年的第一天和最后一天,今天通過本文介紹下Java獲取某年的第一天和最后一天,需要的朋友可以參考下
    2023-07-07
  • Java實(shí)現(xiàn)Token登錄驗(yàn)證的項(xiàng)目實(shí)踐

    Java實(shí)現(xiàn)Token登錄驗(yàn)證的項(xiàng)目實(shí)踐

    本文主要介紹了Java實(shí)現(xiàn)Token登錄驗(yàn)證的項(xiàng)目實(shí)踐,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • 利用Lambda表達(dá)式創(chuàng)建新線程案例

    利用Lambda表達(dá)式創(chuàng)建新線程案例

    這篇文章主要介紹了利用Lambda表達(dá)式創(chuàng)建新線程案例,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • Spring Boot 自動配置的實(shí)現(xiàn)

    Spring Boot 自動配置的實(shí)現(xiàn)

    這篇文章主要介紹了Spring Boot 自動配置的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • Java通過Lambda表達(dá)式實(shí)現(xiàn)簡化代碼

    Java通過Lambda表達(dá)式實(shí)現(xiàn)簡化代碼

    我們在編寫代碼時(shí),常常會遇到代碼又長又重復(fù)的情況,就像調(diào)用第3方服務(wù)時(shí),每個(gè)方法都差不多,?寫起來啰嗦,?改起來麻煩,?還容易改漏,所以本文就來用Lambda表達(dá)式簡化一下代碼,希望對大家有所幫助
    2023-05-05
  • java中Executor,ExecutorService,ThreadPoolExecutor詳解

    java中Executor,ExecutorService,ThreadPoolExecutor詳解

    這篇文章主要介紹了java中Executor,ExecutorService,ThreadPoolExecutor詳解的相關(guān)資料,需要的朋友可以參考下
    2017-02-02
  • Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之時(shí)間復(fù)雜度與空間復(fù)雜度

    Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之時(shí)間復(fù)雜度與空間復(fù)雜度

    對于一個(gè)算法,其時(shí)間復(fù)雜度和空間復(fù)雜度往往是相互影響的,當(dāng)追求一個(gè)較好的時(shí)間復(fù)雜度時(shí),可能會使空間復(fù)雜度的性能變差,即可能導(dǎo)致占用較多的存儲空間,這篇文章主要給大家介紹了關(guān)于Java時(shí)間復(fù)雜度、空間復(fù)雜度的相關(guān)資料,需要的朋友可以參考下
    2022-02-02
  • springboot整合redis過期key監(jiān)聽實(shí)現(xiàn)訂單過期的項(xiàng)目實(shí)踐

    springboot整合redis過期key監(jiān)聽實(shí)現(xiàn)訂單過期的項(xiàng)目實(shí)踐

    現(xiàn)在各種電商平臺都有自己的訂單過期時(shí)間設(shè)置,那么如何設(shè)置訂單時(shí)間過期呢,本文主要介紹了springboot整合redis過期key監(jiān)聽實(shí)現(xiàn)訂單過期的項(xiàng)目實(shí)踐,感興趣的可以了解一下
    2023-12-12
  • java 中 poi解析Excel文件版本問題解決辦法

    java 中 poi解析Excel文件版本問題解決辦法

    這篇文章主要介紹了java 中 poi解析Excel文件版本問題解決辦法的相關(guān)資料,需要的朋友可以參考下
    2017-08-08
  • Spring?Data?JPA實(shí)現(xiàn)數(shù)據(jù)持久化過程詳解

    Spring?Data?JPA實(shí)現(xiàn)數(shù)據(jù)持久化過程詳解

    Spring?Data?JPA是一個(gè)流行的Java持久化框架,它在Java應(yīng)用程序中提供了一種簡單、一致和易于使用的方式來訪問各種數(shù)據(jù)庫。本文將介紹Spring?Data?JPA的基本概念和用法并提供一個(gè)完整的實(shí)例,幫助您更好地理解它的使用方法和優(yōu)勢
    2023-05-05

最新評論

泽州县| 济南市| 锦州市| 垣曲县| 镇安县| 金川县| 莱西市| 渭源县| 富宁县| 城步| 巩义市| 曲靖市| 泰和县| 章丘市| 原阳县| 宜兰县| 仪征市| 社旗县| 榕江县| 汉源县| 长治县| 永清县| 宕昌县| 台湾省| 盐山县| 赫章县| 通化市| 晴隆县| 鄂尔多斯市| 巩留县| 抚顺市| 阳新县| 正定县| 德清县| 绵阳市| 峨山| 台东市| 拉孜县| 阿拉尔市| 内黄县| 新平|