亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

ThreadLocal原理分析及應用場景是怎樣的

發布時間:2021-12-27 09:38:14 來源:億速云 閱讀:119 作者:柒染 欄目:開發技術

本篇文章給大家分享的是有關ThreadLocal原理分析及應用場景是怎樣的,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。

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

首先介紹Thread類中屬性threadLocals:

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

ThreadLocal.ThreadLocalMap threadLocals = null;

我們發現Thread并沒有提供成員變量threadLocals的設置與訪問的方法,那么每個線程的實例threadLocals參數我們如何操作呢?這時我們的主角:ThreadLocal就登場了。

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

也就是說我們對于ThreadLocal的get, set,remove的操作結果都是針對當前線程Thread實例的threadLocals存,取,刪除操作。類似于一個開發者的任務,產品經理左右不了,產品經理只能通過技術leader來給開發者分配任務。下面再舉個栗子,進一步說明他們之間的關系:

ThreadLocal原理分析及應用場景是怎樣的

1.每個人都一張銀行卡

2.每個人每張卡都有一定的余額。

3.每個人獲取銀行卡余額都必須通過該銀行的管理系統。

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

ThreadLocal原理分析及應用場景是怎樣的

映射到我們要說的ThreadLocal

  • 1.card類似于Thread

  • 2.card余額屬性,卡號屬性等類似于Treadlocal內部屬性集合threadLocals

  • 3.cardManager類似于ThreadLocal管理類

那ThreadLocal有哪些應用場景呢?

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

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

2.ThreadLocal如何使用

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

  • 1.我們給每個線程生成一個ID。

  • 2.一旦設置,線程生命周期內不可變化。

  • 3.容器活動期間不可以生成重復的ID

我們創建一個ThreadLocal管理類:

ThreadLocal原理分析及應用場景是怎樣的

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

ThreadLocal原理分析及應用場景是怎樣的

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

ThreadLocal原理分析及應用場景是怎樣的

不出意外我們的結果為:

ThreadLocal原理分析及應用場景是怎樣的

結果:確實是不同線程間id不同,相同線程id相同。

3.ThreadLocal原理

①ThreadLocal類結構及方法解析:

ThreadLocal原理分析及應用場景是怎樣的

上圖可知:ThreadLocal三個方法get, set , remove以及內部類`ThreadLocalMap

②ThreadLocal及Thread之間的關系:

ThreadLocal原理分析及應用場景是怎樣的

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

③ThreadLocal的操作過程:

我們以get方法為例:

ThreadLocal原理分析及應用場景是怎樣的

其中getMap(t)返回的就上當前線程的threadlocals,如下圖,然后根據當前ThreadLocal實例對象作為key獲取ThreadLocalMap中的value,如果首次進來這調用setInitialValue()

ThreadLocal原理分析及應用場景是怎樣的

ThreadLocal原理分析及應用場景是怎樣的

set的過程也類似:

ThreadLocal原理分析及應用場景是怎樣的

注意:ThreadLocal中可以直接t.threadLocals是因為ThreadThreadLocal在同一個包下,同樣Thread可以直接訪問ThreadLocal.ThreadLocalMap threadLocals = null;來進行聲明屬性。

4.ThreadLocal使用有哪些坑及注意事項

我經常在網上看到駭人聽聞的標題,ThreadLocal導致內存泄漏,這通常讓一些剛開始對ThreadLocal理解不透徹的開發者,不敢貿然使用。越不用,越陌生。這樣就讓我們錯失了更好的實現方案,所以敢于引入新技術,敢于踩坑,才能不斷進步。

我們來看下為什么說ThreadLocal會引起內存泄漏,什么場景下會導致內存泄漏?

先回顧下什么叫內存泄漏,對應的什么叫內存溢出

  • ①Memory overflow:內存溢出,沒有足夠的內存提供申請者使用。

  • ②Memory leak:內存泄漏,程序申請內存后,無法釋放已申請的內存空間,內存泄漏的堆積終將導致內存溢出。

顯然是TreadLocal在不規范使用的情況下導致了內存沒有釋放。

ThreadLocal原理分析及應用場景是怎樣的

紅框里我們看到了一個特殊的類WeakReference,同樣這個類,應用開發者也同樣很少使用,這里簡單介紹下吧

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

既然WeakReference在下一次gc即將被回收,那么我們的程序為什么沒有出問題呢?

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

ThreadLocal原理分析及應用場景是怎樣的

這一種存在強引用不會被回收。

ThreadLocal原理分析及應用場景是怎樣的

這里沒有強引用將會被回收。

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

  • ThreadLocal的弱引用回收情況

ThreadLocal原理分析及應用場景是怎樣的

如上圖所示,我們在作為key的ThreadLocal對象沒有外部強引用,下一次gc必將產生key值為null的數據,若線程沒有及時結束必然出現,一條強引用鏈

Threadref–>Thread–>ThreadLocalMap–>Entry,所以這將導致內存泄漏。

下面我們模擬復現ThreadLocal導致內存泄漏:

1.為了效果更佳明顯我們將我們的treadlocals的存儲值value設置為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 {

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

        // 調用
        incrementSameThreadId(memeory);

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

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

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

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

此時我們如何知道內存中存在memory leak呢?

我們可以借助jdk提供的一些命令dump當前堆內存,命令如下:

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

然后我們借助MAT可視化分析工具,來查看對內存,分析對象實例的存活狀態:

ThreadLocal原理分析及應用場景是怎樣的

ThreadLocal原理分析及應用場景是怎樣的

首先打開我們工具提示我們的內存泄漏分析:

ThreadLocal原理分析及應用場景是怎樣的

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

最后我們要確定Entry.key是否還在?打開Dominator Tree,搜索我們的ThreadLocalMemory,發現并沒有存活的實例。

ThreadLocal原理分析及應用場景是怎樣的

ThreadLocal原理分析及應用場景是怎樣的

以上我們復現了ThreadLocal不正當使用,引起的內存泄漏。demo在這里。

所以我們總結了使用ThreadLocal時會發生內存泄漏的前提條件:

  • ThreadLocal引用被設置為null,且后面沒有set,get,remove操作。

  • ②線程一直運行,不停止。(線程池)

  • ③觸發了垃圾回收。(Minor GC或Full GC)

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

  • ①ThreadLocal申明為private static final。
    Private與final 盡可能不讓他人修改變更引用,
    Static 表示為類屬性,只有在程序結束才會被回收。

  • ②ThreadLocal使用后務必調用remove方法。
    最簡單有效的方法是使用后將其移除。

以上就是ThreadLocal原理分析及應用場景是怎樣的,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

高州市| 清河县| 金阳县| 翁源县| 确山县| 自贡市| 中西区| 阳东县| 兰西县| 泰顺县| 明光市| 扎兰屯市| 九江县| 雷州市| 阳东县| 威信县| 洛隆县| 霍邱县| 荣昌县| 和硕县| 石屏县| 来宾市| 吴桥县| 陆良县| 牙克石市| 建宁县| 梅州市| 穆棱市| 灌阳县| 太仆寺旗| 浑源县| 黑山县| 民县| 高阳县| 广汉市| 舞钢市| 岐山县| 宁陵县| 奉化市| 淮安市| 黔西县|