91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

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

ThreadLocal的常用方法、使用場景及注意事項有哪些

發布時間:2021-10-12 10:12:00 來源:億速云 閱讀:145 作者:iii 欄目:開發技術

本篇內容主要講解“ThreadLocal的常用方法、使用場景及注意事項有哪些”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“ThreadLocal的常用方法、使用場景及注意事項有哪些”吧!

目錄
  • 1. ThreadLocal詳解

  • 2. ThreadLocal的使用場景

  • 3.常用方法源碼解析

    • 3.1 initialValue方法

    • 3.2 set(T value)方法

    • 3.3 get方法

    • 3.4 小結

    • 3.4 ThreadLocalMap數據結構

  • 4. ThreadLocal的副作用

    • 4.1 ThreadLocal引起臟數據

    • 4.2 ThreadLocal引起的內存泄漏

  • 5. ThreadLocal內存泄漏解決方案及remove方法源碼解析

    1. ThreadLocal詳解

    JDK1.2版本起,Java就提供了java.lang.ThreadLocal,ThreadLocal為每個使用線程都提供獨立的變量副本,可以做到線程間的數據隔離,每個線程都可以訪問各自內部的副本變量。

    線程上下文ThreadLocal又稱為"線程保險箱",ThreadLocal能夠將指定的變量和當前線程進行綁定,線程之間彼此隔離,持有不同的對象實例,從而避免了數據資源的競爭。

    2. ThreadLocal的使用場景

    • 在進行對象跨層傳遞的時候,可以考慮ThreadLocal,避免方法多次傳遞,打破層次間的約束。

    • 線程間數據隔離。

    • 進行事務操作,用于儲存線程事務信息。

    注意:

    ThreadLocal并不是解決多線程下共享資源的一種技術,一般情況下,每一個線程的ThreadLocal存儲的都是一個全新的對象(通過new關鍵字創建),如果多線程的ThreadLocal存儲了一個對象引用,那么就會面臨資源競爭,數據不一致等并發問題。

    3.常用方法源碼解析

    3.1 initialValue方法

     protected T initialValue() {
            return null;
     }

    此方法為ThreadLocal保存的數據類型指定的一個初始化值,在ThreadLocal中默認返回null。但可以重寫initialValue()方法進行數據初始化。

    如果使用的是Java8提供的Supplier函數接口更加簡化:

    // withInitial()實際是創建了一個ThreadLocal的子類SuppliedThreadLocal,重寫initialValue()
    ThreadLocal<Object> threadLocal = ThreadLocal.withInitial(Object::new);

    3.2 set(T value)方法

    主要存儲指定數據。

    public void set(T value) {
        // 獲取當前線程Thread.currentThread() 
        Thread t = Thread.currentThread();
        // 根據當前線程獲取與之關聯的ThreadLocalMap數據結構
        ThreadLocalMap map = getMap(t);
        if (map != null)
            // 核心方法。set 遍歷整個Entry的過程,后面有詳解
            map.set(this, value);
        else {
            // 調用createMap(),創建ThreadLocalMap,key為當前ThreadLocal實例,存入數據為當前value。
            // ThreadLocal會創建一個默認長度為16Entry節點,并將k-v放入i位置(i位置計算方式和hashmap相似,
            // 當前線程的hashCode&(entry默認長度-1)),并設置閾值(默認為0)為Entry默認長度的2/3。
            createMap(t, value);
        }
    }
    // set 遍歷整個Entry的過程
    private void set(ThreadLocal<?> key, Object value) {
        // 獲取所有的Entry
        Entry[] tab = table;
        int len = tab.length;
        // 根據ThreadLocal對象,計算角標位置
        int i = key.threadLocalHashCode & (len-1);
     // 循環查找
        for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {
            ThreadLocal<?> k = e.get();
      // 找到相同的就直接覆蓋,直接返回。
            if (k == key) {
                e.value = value;
                return;
            }
      // 如果ThreadLocal為null,直接驅出并使用新數據(Value)占居原來位置,
      // 這個過程主要是防止內存泄漏。
            if (k == null) {
                // 驅除ThreadLocal為null的Entry,并放入Value,這也是內存泄漏的重點地區
                replaceStaleEntry(key, value, i);
                return;
            }
        }
     // entry都為null,創建新的entry,已ThreadLocal為key,將存放數據為Value。
        tab[i] = new Entry(key, value);
        int sz = ++size;
        // ThreadLoaclMapde的當前數據元素的個數和閾值比較,再次進行key為null的清理工作。
        if (!cleanSomeSlots(i, sz) && sz >= threshold)
            // 整理Entry,當Entry中的ThreadLocal對象為null時,通過重新計算角標位來清理
            // 以前ThreadLocal。如果Entry數量大于3/4容量進行擴容
            rehash();
    }

    3.3 get方法

    get()用于返回當前線程ThreadLocal中數據備份,當前線程的數據都存在一個ThreadLocalMap的數據結構中。

    public T get() {
        Thread t = Thread.currentThread();
        // 獲得ThreadLocalMap對象map,ThreadLocalMap是和當前Thread關聯的,
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            // 存入ThreadLocal中的數據實際上是存儲在ThreadLocalMap的Entry中。
            // 而此Entry是放在一個Entry數組里面的。
            // 獲取當前ThreadLocal對應的entry
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                // 直接返回當前數據 
                T result = (T)e.value;
                return result;
            }
        }
        // ThreadLocalMap未初始化,首先初始化
        return setInitialValue();
    }
    // ThreadLocal的setInitialValue方法源碼
    private T setInitialValue() {
        // 為ThreadLocalMap指定Value的初始化值
        T value = initialValue();
        Thread t = Thread.currentThread();
        // 根據本地線程Thread獲取ThreadLocalMap,一下方法與Set方法相同。
        ThreadLocalMap map = getMap(t);
        if (map != null)
            // 如果map存在,直接調用set()方法進行賦值。
            map.set(this, value);
        else
            // map==null;創建ThreadLocalMap對象,并將Thread和value關聯起來
            createMap(t, value);
        return value;
    }

    3.4 小結

    • initialValue():初始化ThreadLocal中的value屬性值。

    • set():獲取當前線程,根據當前線程從ThreadLocals中獲取ThreadLocalMap數據結構,

      • 如果ThreadLocalmap的數據結構沒創建,則創建ThreadLocalMap,key為當前ThreadLocal實例,存入數據為當前value。ThreadLocal會創建一個默認長度為16Entry節點,并將k-v放入i位置(i位置計算方式和hashmap相似,當前線程的hashCode&(entry默認長度-1)),并設置閾值(默認為0)為Entry默認長度的2/3。

      • 如果ThreadLocalMap存在。就會遍歷整個Map中的Entry節點,如果entry中的key和本線程ThreadLocal相同,將數據(value)直接覆蓋,并返回。如果ThreadLoca為null,驅除ThreadLocal為null的Entry,并放入Value,這也是內存泄漏的重點地區。

    • get()

    • get()方法比較簡單。就是根據Thread獲取ThreadLocalMap。通過ThreadLocal來獲得數據value。注意的是:如果ThreadLocalMap沒有創建,直接進入創建過程。初始化ThreadLocalMap。并直接調用和set方法一樣的方法。

    3.4 ThreadLocalMap數據結構

    set()還是get()方法都是避免不了和ThreadLocalMap和Entry打交道。ThreadLocalMap是一個類似于HashMap的一個數據結構(沒有鏈表),僅僅用于存放線程存放在ThreadLocal中的數據備份,ThreadLocalMap的所有方法對外部都是不可見的。

    ThreadLocalMap中用于存儲數據的Entry,它是一個WeakReference類型的子類,之所以設計成WeakReference是為了能夠是JVM發生gc,能夠自動回收,防止內存溢出現象。

    4. ThreadLocal的副作用

    4.1 ThreadLocal引起臟數據

    線程復用會產生臟數據。

    由于結程池會重用 Thread 對象 ,那么與 Thread 綁定的類的靜態屬性 ThreadLocal 變量也會被重用。如果在實現的線程 run()方法體中不顯式地調用 remove() 清理與線程相關的ThreadLocal 信息,那么如果下一個線程不調用set()設置初始值,就可能 get()到重用的線程信息,包括 ThreadLocal 所關聯的線程對象的 value 值。

    // java.lang.Thread#threadLocals
      /* ThreadLocal values pertaining to this thread. This map is maintained
         * by the ThreadLocal class. */
      ThreadLocal.ThreadLocalMap threadLocals = null;

    4.2 ThreadLocal引起的內存泄漏

    在上面提到ThreadLocalMap中存放的Entry是WeakReference的子類。所以在JVM觸發GC(young gc,Full GC)時,都會導致Entry的回收

    在get數據的時候,增加檢查,清除已經被回收器回收的Entry(WeakReference可以自動回收)

    private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
        ThreadLocal<?> k = e.get();
      ...
        if (k == null)
            // 清除 key 是 null 的Entry
            expungeStaleEntry(i);
      ...
     return null;
    }
    private boolean cleanSomeSlots(int i, int n) {
        boolean removed = false;
        Entry[] tab = table;
        int len = tab.length;
        do {
            i = nextIndex(i, len);
            Entry e = tab[i];
            if (e != null && e.get() == null) {
                n = len;
                removed = true;
                // 清除key==null 的Entry
                i = expungeStaleEntry(i);
            }
        } while ( (n >>>= 1) != 0);
        return removed;
    }

    set數據時增加檢查,刪除已經被垃圾回收器清理的Entry,并將其移除

    private boolean cleanSomeSlots(int i, int n) {
        boolean removed = false;
        Entry[] tab = table;
        int len = tab.length;
        do {
            i = nextIndex(i, len);
            Entry e = tab[i];
            if (e != null && e.get() == null) {
                n = len;
                removed = true;
                // 清除key==null 的Entry
                i = expungeStaleEntry(i);
            }
        } while ( (n >>>= 1) != 0);
        return removed;
    }

    基于上面三點:ThreadLocal在一定程度上保證不會發生內存泄漏。但是Thread類中有ThreadlocalMap的引用,導致對象的可達性,故不能回收。

    ThreadLocal被置為null清除了。但是通過ThreadLocalMap還是被Thread類引用。導致該數據是可達的。所以內存得不到釋放,除非當前線程結束,Thread引用就會被垃圾回收器回收。如圖所示

    ThreadLocal的常用方法、使用場景及注意事項有哪些

    ThreadLocal的常用方法、使用場景及注意事項有哪些5

    5. ThreadLocal內存泄漏解決方案及remove方法源碼解析

    解決ThreadLocal內存泄漏的常用方法是:在使用完ThreadLocal之后,及時remove掉。

    public void remove() {
        // 根據當前線程,獲取ThreadLocalMap
        ThreadLocalMap m = getMap(Thread.currentThread());
        if (m != null)
            // map不為null,執行remove操作
            m.remove(this);
    }
    // ThreadLocal 的remove()
    private void remove(ThreadLocal<?> key) {
        // 獲取存放key-value的數組。
        Entry[] tab = table;
        int len = tab.length;
        // 根據ThreadLocal的HashCode確定唯一的角標
        int i = key.threadLocalHashCode & (len-1);
        for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {
            if (e.get() == key) {
                // 如果和本ThreadLocal相同。將引用置null。
                e.clear();
                // 實行Enty和Entry.value置null。源碼中 tab[staleSlot].value = null; tab[staleSlot] = null;
                expungeStaleEntry(i);
                return;
            }
        }
    }

    到此,相信大家對“ThreadLocal的常用方法、使用場景及注意事項有哪些”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

    向AI問一下細節

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

    AI

    赤城县| 城市| 馆陶县| 丹阳市| 穆棱市| 化隆| 阳朔县| 库车县| 巴彦县| 光泽县| 巴东县| 山丹县| 达日县| 商洛市| 边坝县| 内江市| 乐昌市| 石台县| 岳西县| 翼城县| 米泉市| 思南县| 南昌市| 印江| 沙坪坝区| 麻城市| 沙河市| 石阡县| 延寿县| 隆回县| 尚义县| 浙江省| 蓬安县| 永宁县| 昌图县| 鹿邑县| 台南县| 哈巴河县| 青浦区| 紫金县| 南华县|