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

深入理解Netty?FastThreadLocal優(yōu)缺點(diǎn)及實(shí)現(xiàn)邏輯

 更新時(shí)間:2023年10月23日 10:59:29   作者:vivo互聯(lián)網(wǎng)技術(shù)  
本文以線上詭異問題為切入點(diǎn),通過對比JDK ThreadLocal和Netty FastThreadLocal實(shí)現(xiàn)邏輯以及優(yōu)缺點(diǎn),并深入解讀源碼,由淺入深理解Netty FastThreadLocal

一、前言

最近在學(xué)習(xí)Netty相關(guān)的知識,在看到Netty FastThreadLocal章節(jié)中,回想起一起線上詭異問題。

  • 問題描述:外銷業(yè)務(wù)獲取用戶信息判斷是否支持https場景下,獲取的用戶信息有時(shí)候竟然是錯(cuò)亂的。
  • 問題分析:使用ThreadLocal保存用戶信息時(shí),未能及時(shí)進(jìn)行remove()操作,而Tomcat工作線程是基于線程池的,會(huì)出現(xiàn)線程重用情況,所以獲取的用戶信息可能是之前線程遺留下來的。
  • 問題修復(fù):ThreadLocal使用完之后及時(shí)remove()、ThreadLocal使用之前也進(jìn)行remove()雙重保險(xiǎn)操作。

接下來,我們繼續(xù)深入了解下JDK ThreadLocal和Netty FastThreadLocal吧。

二、JDK ThreadLocal介紹

ThreadLocal是JDK提供的一個(gè)方便對象在本線程內(nèi)不同方法中傳遞、獲取的類。用它定義的變量,僅在本線程中可見,不受其他線程的影響,與其他線程相互隔離。

那具體是如何實(shí)現(xiàn)的呢?如圖1所示,每個(gè)線程都會(huì)有個(gè)ThreadLocalMap實(shí)例變量,其采用懶加載的方式進(jìn)行創(chuàng)建,當(dāng)線程第一次訪問此變量時(shí)才會(huì)去創(chuàng)建。

ThreadLocalMap使用線性探測法存儲(chǔ)ThreadLocal對象及其維護(hù)的數(shù)據(jù),具體操作邏輯如下:

  • 假設(shè)有一個(gè)新的ThreadLocal對象,通過hash計(jì)算它應(yīng)存儲(chǔ)的位置下標(biāo)為x。
  • 此時(shí)發(fā)現(xiàn)下標(biāo)x對應(yīng)位置已經(jīng)存儲(chǔ)了其他的ThreadLocal對象,則它會(huì)往后尋找,步長為1,下標(biāo)變更為x+1。
  • 接下來發(fā)現(xiàn)下標(biāo)x+1對應(yīng)位置也已經(jīng)存儲(chǔ)了其他的ThreadLocal對象,同理則它會(huì)繼續(xù)往后尋找,下標(biāo)變更為x+2。
  • 直到尋找到下標(biāo)為x+3時(shí)發(fā)現(xiàn)是空閑的,然后將該ThreadLocal對象及其維護(hù)的數(shù)據(jù)構(gòu)建一個(gè)entry對象存儲(chǔ)在x+3位置。

在ThreadLocalMap中數(shù)據(jù)很多的情況下,很容易出現(xiàn)hash沖突,解決沖突需要不斷的向下遍歷,該操作的時(shí)間復(fù)雜度為O(n),效率較低。

圖1

從下面的代碼中可以看出:

Entry 的 key 是弱引用,value 是強(qiáng)引用。在 JVM 垃圾回收時(shí),只要發(fā)現(xiàn)弱引用的對象,不管內(nèi)存是否充足,都會(huì)被回收。

但是當(dāng) ThreadLocal 不再使用被 GC 回收后,ThreadLocalMap 中可能出現(xiàn) Entry 的 key 為 NULL,那么 Entry 的 value 一直會(huì)強(qiáng)引用數(shù)據(jù)而得不到釋放,只能等待線程銷毀,從而造成內(nèi)存泄漏。

static class ThreadLocalMap {
    // 弱引用,在資源緊張的時(shí)候可以回收部分不再引用的ThreadLocal變量
    static class Entry extends WeakReference<ThreadLocal<?>> {
        // 當(dāng)前ThreadLocal對象所維護(hù)的數(shù)據(jù)
        Object value;
 
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v;
        }
    }
    // 省略其他代碼
}

綜上所述,既然JDK提供的ThreadLocal可能存在效率較低和內(nèi)存泄漏的問題,為啥不做相應(yīng)的優(yōu)化和改造呢?

  • 從ThreadLocal類注釋看,它是JDK1.2版本引入的,早期可能不太關(guān)注程序的性能。
  • 大部分多線程場景下,線程中的ThreadLocal變量較少,因此出現(xiàn)hash沖突的概率相對較小,及時(shí)偶爾出現(xiàn)了hash沖突,對程序的性能影響也相對較小。
  • 對于內(nèi)存泄漏問題,ThreadLocal本身已經(jīng)做了一定的保護(hù)措施。作為使用者,在線程中某個(gè)ThreadLocal對象不再使用或出現(xiàn)異常時(shí),立即調(diào)用 remove() 方法刪除 Entry 對象,養(yǎng)成良好的編碼習(xí)慣。

三、Netty FastThreadLocal介紹

FastThreadLocal是Netty中對JDK提供的ThreadLocal優(yōu)化改造版本,從名稱上來看,它應(yīng)該比ThreadLocal更快了,以應(yīng)對Netty處理并發(fā)量大、數(shù)據(jù)吞吐量大的場景。

那具體是如何實(shí)現(xiàn)的呢?如圖2所示,每個(gè)線程都會(huì)有個(gè)InternalThreadLocalMap實(shí)例變量。

每個(gè)FastThreadLocal實(shí)例創(chuàng)建時(shí),都會(huì)采用AtomicInteger保證順序遞增生成一個(gè)不重復(fù)的下標(biāo)index,它是該FastThreadLocal對象維護(hù)的數(shù)據(jù)應(yīng)該存儲(chǔ)的位置。

讀寫數(shù)據(jù)的時(shí)候通過FastThreadLocal的下標(biāo) index 直接定位到該FastThreadLocal的位置,時(shí)間復(fù)雜度為 O(1),效率較高。

如果該下標(biāo)index遞增到特別大,InternalThreadLocalMap維護(hù)的數(shù)組也會(huì)特別大,所以FastThreadLocal是通過空間換時(shí)間來提升讀寫性能的。

圖2

四、Netty FastThreadLocal源碼分析

4.1 構(gòu)造方法

public class FastThreadLocal<V> {
    // FastThreadLocal中的index是記錄了該它維護(hù)的數(shù)據(jù)應(yīng)該存儲(chǔ)的位置
    // InternalThreadLocalMap數(shù)組中的下標(biāo), 它是在構(gòu)造函數(shù)中確定的
    private final int index;
 
    public InternalThreadLocal() {
        index = InternalThreadLocalMap.nextVariableIndex();
    }
    // 省略其他代碼
}
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
    // 自增索引, ?于計(jì)算下次存儲(chǔ)到Object數(shù)組中的位置
    private static final AtomicInteger nextIndex = new AtomicInteger();
    private static final int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8;
    public static int nextVariableIndex() {
        int index = nextIndex.getAndIncrement();
        if (index >= ARRAY_LIST_CAPACITY_MAX_SIZE || index < 0) {
            nextIndex.set(ARRAY_LIST_CAPACITY_MAX_SIZE);
            throw new IllegalStateException("too many thread-local indexed variables");
        }
        return index;
    }
    // 省略其他代碼
}

上面這兩段代碼在Netty FastThreadLocal介紹中已經(jīng)講解過,這邊就不再重復(fù)介紹了。

4.2 get 方法

public class FastThreadLocal<V> {
    // FastThreadLocal中的index是記錄了該它維護(hù)的數(shù)據(jù)應(yīng)該存儲(chǔ)的位置
    private final int index;
 
    public final V get() {
        // 獲取當(dāng)前線程的InternalThreadLocalMap
        InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
        // 根據(jù)當(dāng)前線程的index從InternalThreadLocalMap中獲取其綁定的數(shù)據(jù)
        Object v = threadLocalMap.indexedVariable(index);
        // 如果獲取當(dāng)前線程綁定的數(shù)據(jù)不為缺省值UNSET,則直接返回;否則進(jìn)行初始化
        if (v != InternalThreadLocalMap.UNSET) {
            return (V) v;
        }
 
        return initialize(threadLocalMap);
    }
    // 省略其他代碼
}
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
    private static final int INDEXED_VARIABLE_TABLE_INITIAL_SIZE = 32;
    // 未賦值的Object變量(缺省值),當(dāng)?個(gè)與線程綁定的值被刪除之后,會(huì)被設(shè)置為UNSET
    public static final Object UNSET = new Object();
    // 存儲(chǔ)綁定到當(dāng)前線程的數(shù)據(jù)的數(shù)組
    private Object[] indexedVariables;
    // slowThreadLocalMap為JDK ThreadLocal存儲(chǔ)InternalThreadLocalMap
    private static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap =
            new ThreadLocal<InternalThreadLocalMap>();
    // 從綁定到當(dāng)前線程的數(shù)據(jù)的數(shù)組中取出index位置的元素
    public Object indexedVariable(int index) {
        Object[] lookup = indexedVariables;
        return index < lookup.length? lookup[index] : UNSET;
    }
    public static InternalThreadLocalMap get() {
        Thread thread = Thread.currentThread();
        // 判斷當(dāng)前線程是否是FastThreadLocalThread類型
        if (thread instanceof FastThreadLocalThread) {
            return fastGet((FastThreadLocalThread) thread);
        } else {
            return slowGet();
        }
    }
    private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
        // 直接獲取當(dāng)前線程的InternalThreadLocalMap
        InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
        // 如果當(dāng)前線程的InternalThreadLocalMap還未創(chuàng)建,則創(chuàng)建并賦值
        if (threadLocalMap == null) {
            thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
        }
        return threadLocalMap;
    }
    private static InternalThreadLocalMap slowGet() {
        // 使用JDK ThreadLocal獲取InternalThreadLocalMap
        InternalThreadLocalMap ret = slowThreadLocalMap.get();
        if (ret == null) {
            ret = new InternalThreadLocalMap();
            slowThreadLocalMap.set(ret);
        }
        return ret;
    }
    private InternalThreadLocalMap() {
        indexedVariables = newIndexedVariableTable();
    }
    // 初始化一個(gè)32位長度的Object數(shù)組,并將其元素全部設(shè)置為缺省值UNSET
    private static Object[] newIndexedVariableTable() {
        Object[] array = new Object[INDEXED_VARIABLE_TABLE_INITIAL_SIZE];
        Arrays.fill(array, UNSET);
        return array;
    }
    // 省略其他代碼
}

源碼中 get() 方法主要分為下面3個(gè)步驟處理:

  • 通過InternalThreadLocalMap.get()方法獲取當(dāng)前線程的InternalThreadLocalMap。
  • 根據(jù)當(dāng)前線程的index 從InternalThreadLocalMap中獲取其綁定的數(shù)據(jù)。
  • 如果不是缺省值UNSET,直接返回;如果是缺省值,則執(zhí)行initialize方法進(jìn)行初始化。

下面我們繼續(xù)分析一下 InternalThreadLocalMap.get() 方法的實(shí)現(xiàn)邏輯。

  • 首先判斷當(dāng)前線程是否是FastThreadLocalThread類型,如果是FastThreadLocalThread類型則直接使用fastGet方法獲取InternalThreadLocalMap,如果不是FastThreadLocalThread類型則使用slowGet方法獲取InternalThreadLocalMap兜底處理。
  • 兜底處理中的slowGet方法會(huì)退化成JDK原生的ThreadLocal獲取InternalThreadLocalMap。
  • 獲取InternalThreadLocalMap時(shí),如果為null,則會(huì)直接創(chuàng)建一個(gè)InternalThreadLocalMap返回。其創(chuàng)建過過程中初始化一個(gè)32位長度的Object數(shù)組,并將其元素全部設(shè)置為缺省值UNSET。

4.3 set 方法

public class FastThreadLocal<V> {
    // FastThreadLocal初始化時(shí)variablesToRemoveIndex被賦值為0
    private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
    public final void set(V value) {
        // 判斷value值是否是未賦值的Object變量(缺省值)
        if (value != InternalThreadLocalMap.UNSET) {
            // 獲取當(dāng)前線程對應(yīng)的InternalThreadLocalMap
            InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
            // 將InternalThreadLocalMap中數(shù)據(jù)替換為新的value
            // 并將FastThreadLocal對象保存到待清理的Set中
            setKnownNotUnset(threadLocalMap, value);
        } else {
            remove();
        }
    }
    private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
        // 將InternalThreadLocalMap中數(shù)據(jù)替換為新的value
        if (threadLocalMap.setIndexedVariable(index, value)) {
            // 并將當(dāng)前的FastThreadLocal對象保存到待清理的Set中
            addToVariablesToRemove(threadLocalMap, this);
        }
    }
    private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
        // 取下標(biāo)index為0的數(shù)據(jù),用于存儲(chǔ)待清理的FastThreadLocal對象Set集合中
        Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
        Set<FastThreadLocal<?>> variablesToRemove;
        if (v == InternalThreadLocalMap.UNSET || v == null) {
            // 下標(biāo)index為0的數(shù)據(jù)為空,則創(chuàng)建FastThreadLocal對象Set集合
            variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>());
            // 將InternalThreadLocalMap中下標(biāo)為0的數(shù)據(jù),設(shè)置成FastThreadLocal對象Set集合
            threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);
        } else {
            variablesToRemove = (Set<FastThreadLocal<?>>) v;
        }
        // 將FastThreadLocal對象保存到待清理的Set中
        variablesToRemove.add(variable);
    }
    // 省略其他代碼
}
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
    // 未賦值的Object變量(缺省值),當(dāng)?個(gè)與線程綁定的值被刪除之后,會(huì)被設(shè)置為UNSET
    public static final Object UNSET = new Object();
    // 存儲(chǔ)綁定到當(dāng)前線程的數(shù)據(jù)的數(shù)組
    private Object[] indexedVariables;
    // 綁定到當(dāng)前線程的數(shù)據(jù)的數(shù)組能再次采用x2擴(kuò)容的最大量
    private static final int ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD = 1 << 30;
    private static final int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8;
    // 將InternalThreadLocalMap中數(shù)據(jù)替換為新的value
    public boolean setIndexedVariable(int index, Object value) {
        Object[] lookup = indexedVariables;
        if (index < lookup.length) {
            Object oldValue = lookup[index];
            // 直接將數(shù)組 index 位置設(shè)置為 value,時(shí)間復(fù)雜度為 O(1)
            lookup[index] = value;
            return oldValue == UNSET;
        } else { // 綁定到當(dāng)前線程的數(shù)據(jù)的數(shù)組需要擴(kuò)容,則擴(kuò)容數(shù)組并數(shù)組設(shè)置新value
            expandIndexedVariableTableAndSet(index, value);
            return true;
        }
    }
    private void expandIndexedVariableTableAndSet(int index, Object value) {
        Object[] oldArray = indexedVariables;
        final int oldCapacity = oldArray.length;
        int newCapacity;
        // 判斷可進(jìn)行x2方式進(jìn)行擴(kuò)容
        if (index < ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD) {
            newCapacity = index;
            // 位操作,提升擴(kuò)容效率
            newCapacity |= newCapacity >>>  1;
            newCapacity |= newCapacity >>>  2;
            newCapacity |= newCapacity >>>  4;
            newCapacity |= newCapacity >>>  8;
            newCapacity |= newCapacity >>> 16;
            newCapacity ++;
        } else { // 不支持x2方式擴(kuò)容,則設(shè)置綁定到當(dāng)前線程的數(shù)據(jù)的數(shù)組容量為最大值
            newCapacity = ARRAY_LIST_CAPACITY_MAX_SIZE;
        }
        // 按擴(kuò)容后的大小創(chuàng)建新數(shù)組,并將老數(shù)組數(shù)據(jù)copy到新數(shù)組
        Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
        // 新數(shù)組擴(kuò)容后的部分賦UNSET缺省值
        Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
        // 新數(shù)組的index位置替換成新的value
        newArray[index] = value;
        // 綁定到當(dāng)前線程的數(shù)據(jù)的數(shù)組用新數(shù)組替換
        indexedVariables = newArray;
    }
    // 省略其他代碼
}

源碼中 set() 方法主要分為下面3個(gè)步驟處理:

  • 判斷value是否是缺省值UNSET,如果value不等于缺省值,則會(huì)通過InternalThreadLocalMap.get()方法獲取當(dāng)前線程的InternalThreadLocalMap,具體實(shí)現(xiàn)3.2小節(jié)中g(shù)et()方法已做講解。
  • 通過FastThreadLocal中的setKnownNotUnset()方法將InternalThreadLocalMap中數(shù)據(jù)替換為新的value,并將當(dāng)前的FastThreadLocal對象保存到待清理的Set中。
  • 如果等于缺省值UNSET或null(else的邏輯),會(huì)調(diào)用remove()方法,remove()具體見后面的代碼分析。

接下來我們看下 InternalThreadLocalMap.setIndexedVariable 方法的實(shí)現(xiàn)邏輯。

  • 判斷index是否超出存儲(chǔ)綁定到當(dāng)前線程的數(shù)據(jù)的數(shù)組indexedVariables的長度,如果沒有超出,則獲取index位置的數(shù)據(jù),并將該數(shù)組index位置數(shù)據(jù)設(shè)置新value。
  • 如果超出了,綁定到當(dāng)前線程的數(shù)據(jù)的數(shù)組需要擴(kuò)容,則擴(kuò)容該數(shù)組并將它index位置的數(shù)據(jù)設(shè)置新value。
  • 擴(kuò)容數(shù)組以index 為基準(zhǔn)進(jìn)行擴(kuò)容,將數(shù)組擴(kuò)容后的容量向上取整為 2 的次冪。然后將原數(shù)組內(nèi)容拷貝到新的數(shù)組中,空余部分填充缺省值UNSET,最終把新數(shù)組賦值給 indexedVariables。

下面我們再繼續(xù)看下 FastThreadLocal.addToVariablesToRemove 方法的實(shí)現(xiàn)邏輯。

  • 取下標(biāo)index為0的數(shù)據(jù)(用于存儲(chǔ)待清理的FastThreadLocal對象Set集合中),如果該數(shù)據(jù)是缺省值UNSET或null,則會(huì)創(chuàng)建FastThreadLocal對象Set集合,并將該Set集合填充到下標(biāo)index為0的數(shù)組位置。
  • 如果該數(shù)據(jù)不是缺省值UNSET,說明Set集合已金被填充,直接強(qiáng)轉(zhuǎn)獲取該Set集合。
  • 最后將FastThreadLocal對象保存到待清理的Set集合中。

4.4 remove、removeAll方法

public class FastThreadLocal<V> {
    // FastThreadLocal初始化時(shí)variablesToRemoveIndex被賦值為0
    private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
    public final void remove() {
        // 獲取當(dāng)前線程的InternalThreadLocalMap
        // 刪除當(dāng)前的FastThreadLocal對象及其維護(hù)的數(shù)據(jù)
        remove(InternalThreadLocalMap.getIfSet());
    }
    public final void remove(InternalThreadLocalMap threadLocalMap) {
        if (threadLocalMap == null) {
            return;
        }
        // 根據(jù)當(dāng)前線程的index,并將該數(shù)組下標(biāo)index位置對應(yīng)的值設(shè)置為缺省值UNSET
        Object v = threadLocalMap.removeIndexedVariable(index);
        // 存儲(chǔ)待清理的FastThreadLocal對象Set集合中刪除當(dāng)前FastThreadLocal對象
        removeFromVariablesToRemove(threadLocalMap, this);
        if (v != InternalThreadLocalMap.UNSET) {
            try {
                // 空方法,用戶可以繼承實(shí)現(xiàn)
                onRemoval((V) v);
            } catch (Exception e) {
                PlatformDependent.throwException(e);
            }
        }
    }
    public static void removeAll() {
        InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
        if (threadLocalMap == null) {
            return;
        }
        try {
            // 取下標(biāo)index為0的數(shù)據(jù),用于存儲(chǔ)待清理的FastThreadLocal對象Set集合中
            Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
            if (v != null && v != InternalThreadLocalMap.UNSET) {
                @SuppressWarnings("unchecked")
                Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
                // 遍歷所有的FastThreadLocal對象并刪除它們以及它們維護(hù)的數(shù)據(jù)
                FastThreadLocal<?>[] variablesToRemoveArray =
                        variablesToRemove.toArray(new FastThreadLocal[0]);
                for (FastThreadLocal<?> tlv: variablesToRemoveArray) {
                    tlv.remove(threadLocalMap);
                }
            }
        } finally {
            // 刪除InternalThreadLocalMap中threadLocalMap和slowThreadLocalMap數(shù)據(jù)
            InternalThreadLocalMap.remove();
        }
    }
    private static void removeFromVariablesToRemove(
            InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
        // 取下標(biāo)index為0的數(shù)據(jù),用于存儲(chǔ)待清理的FastThreadLocal對象Set集合中
        Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
        if (v == InternalThreadLocalMap.UNSET || v == null) {
            return;
        }
        @SuppressWarnings("unchecked")
        // 存儲(chǔ)待清理的FastThreadLocal對象Set集合中刪除該FastThreadLocal對象
        Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v;
        variablesToRemove.remove(variable);
    }
    // 省略其他代碼
}
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
    // 根據(jù)當(dāng)前線程獲取InternalThreadLocalMap
       public static InternalThreadLocalMap getIfSet() {
        Thread thread = Thread.currentThread();
        if (thread instanceof FastThreadLocalThread) {
            return ((FastThreadLocalThread) thread).threadLocalMap();
        }
        return slowThreadLocalMap.get();
    }
    // 數(shù)組下標(biāo)index位置對應(yīng)的值設(shè)置為缺省值UNSET
    public Object removeIndexedVariable(int index) {
        Object[] lookup = indexedVariables;
        if (index < lookup.length) {
            Object v = lookup[index];
            lookup[index] = UNSET;
            return v;
        } else {
            return UNSET;
        }
    }
    // 刪除threadLocalMap和slowThreadLocalMap數(shù)據(jù)
    public static void remove() {
        Thread thread = Thread.currentThread();
        if (thread instanceof FastThreadLocalThread) {
            ((FastThreadLocalThread) thread).setThreadLocalMap(null);
        } else {
            slowThreadLocalMap.remove();
        }
    }
    // 省略其他代碼
}

源碼中 remove() 方法主要分為下面2個(gè)步驟處理:

  • 通過InternalThreadLocalMap.getIfSet()獲取當(dāng)前線程的InternalThreadLocalMap。具體和3.2小節(jié)get()方法里面獲取當(dāng)前線程的InternalThreadLocalMap相似,這里就不再重復(fù)介紹了。
  • 刪除當(dāng)前的FastThreadLocal對象及其維護(hù)的數(shù)據(jù)。

源碼中 removeAll() 方法主要分為下面3個(gè)步驟處理:

  • 通過InternalThreadLocalMap.getIfSet()獲取當(dāng)前線程的InternalThreadLocalMap。
  • 取下標(biāo)index為0的數(shù)據(jù)(用于存儲(chǔ)待清理的FastThreadLocal對象Set集合),然后遍歷所有的FastThreadLocal對象并刪除它們以及它們維護(hù)的數(shù)據(jù)。
  • 最后會(huì)將InternalThreadLocalMap本身從線程中移除。

五、總結(jié)

那么使用ThreadLocal時(shí)最佳實(shí)踐又如何呢?

每次使用完ThreadLocal實(shí)例,在線程運(yùn)行結(jié)束之前的finally代碼塊中主動(dòng)調(diào)用它的remove()方法,清除Entry中的數(shù)據(jù),避免操作不當(dāng)導(dǎo)致的內(nèi)存泄漏。

使?Netty的FastThreadLocal一定比JDK原生的ThreadLocal更快嗎?

不?定。當(dāng)線程是FastThreadLocalThread,則添加、獲取FastThreadLocal所維護(hù)數(shù)據(jù)的時(shí)間復(fù)雜度是 O(1),?使?ThreadLocal可能存在哈希沖突,相對來說使?FastThreadLocal更?效。但如果是普通線程則可能更慢。

使?FastThreadLocal有哪些優(yōu)點(diǎn)?

正如文章開頭介紹JDK原生ThreadLocal存在的缺點(diǎn),F(xiàn)astThreadLocal全部優(yōu)化了,它更?效、而且如果使?的是FastThreadLocal,它會(huì)在任務(wù)執(zhí)?完成后主動(dòng)調(diào)?removeAll?法清除數(shù)據(jù),避免潛在的內(nèi)存泄露。

以上就是深入理解Netty FastThreadLocal的詳細(xì)內(nèi)容,更多關(guān)于Netty FastThreadLocal的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java實(shí)現(xiàn)PDF轉(zhuǎn)HTML/Word/Excel/PPT/PNG的示例代碼

    Java實(shí)現(xiàn)PDF轉(zhuǎn)HTML/Word/Excel/PPT/PNG的示例代碼

    這篇文章主要為大家介紹了如何利用Java語言是PDF轉(zhuǎn)HTML、Word、Excel、PPT和PNG功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-05-05
  • Java編程反射機(jī)制用法入門與實(shí)例總結(jié)

    Java編程反射機(jī)制用法入門與實(shí)例總結(jié)

    這篇文章主要介紹了Java編程反射機(jī)制用法,簡單說明了反射機(jī)制的概念、原理并結(jié)合實(shí)例形式總結(jié)分析了java反射機(jī)制的簡單使用方法與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-12-12
  • Java讀寫Windows共享文件夾的方法實(shí)例

    Java讀寫Windows共享文件夾的方法實(shí)例

    本篇文章主要介紹了Java讀寫Windows共享文件夾的方法實(shí)例,具有一定的參考價(jià)值,有興趣的同學(xué)可以了解一下。
    2016-11-11
  • Spring中@Async的使用小結(jié)

    Spring中@Async的使用小結(jié)

    在Java開發(fā)中,我們常常會(huì)遇到需要執(zhí)行耗時(shí)操作的場景,例如文件上傳、網(wǎng)絡(luò)請求等,本文將介紹如何在Java中使用異步方法,并探討其中的一些注意事項(xiàng),感興趣的朋友跟隨小編一起看看吧
    2024-01-01
  • Java中的靜態(tài)內(nèi)部類詳解及代碼示例

    Java中的靜態(tài)內(nèi)部類詳解及代碼示例

    這篇文章主要介紹了Java中的靜態(tài)內(nèi)部類詳解及代碼示例,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-10-10
  • java實(shí)現(xiàn)電話本管理系統(tǒng)

    java實(shí)現(xiàn)電話本管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了JAVA實(shí)現(xiàn)電話本管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • Java調(diào)用Python代碼的幾種方法小結(jié)

    Java調(diào)用Python代碼的幾種方法小結(jié)

    Python語言有豐富的系統(tǒng)管理、數(shù)據(jù)處理、統(tǒng)計(jì)類軟件包,因此從java應(yīng)用中調(diào)用Python代碼的需求很常見、實(shí)用,本文介紹幾種方法從java調(diào)用Python代碼,從而最大化利用兩個(gè)語言的優(yōu)勢,需要的朋友可以參考下
    2025-01-01
  • 詳解Log4j 日志文件存放位置設(shè)置

    詳解Log4j 日志文件存放位置設(shè)置

    這篇文章主要介紹了詳解Log4j 日志文件存放位置設(shè)置,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-08-08
  • java如何根據(jù)模板導(dǎo)出數(shù)據(jù)到word文檔中(表格、自定義標(biāo)簽等)

    java如何根據(jù)模板導(dǎo)出數(shù)據(jù)到word文檔中(表格、自定義標(biāo)簽等)

    這篇文章主要介紹了關(guān)于java如何根據(jù)模板導(dǎo)出數(shù)據(jù)到word文檔中(表格、自定義標(biāo)簽等)的相關(guān)資料,主要包括創(chuàng)建docx文檔,配置模板信息,以及利用XDocReport+FreeMarker技術(shù)進(jìn)行實(shí)現(xiàn),詳細(xì)介紹了在Word模板中如何設(shè)置字段以及如何通過代碼填充這些字段,需要的朋友可以參考下
    2024-11-11
  • spring中@ControllerAdvice 注解的使用

    spring中@ControllerAdvice 注解的使用

    @ControllerAdvice注解是Spring3.2中新增的注解,主要用于Controller的全局配置,本文就來介紹一下spring中@ControllerAdvice 注解的使用,感興趣的可以了解一下
    2024-09-09

最新評論

云阳县| 巫山县| 定远县| 阳高县| 梁山县| 平昌县| 吴桥县| 常山县| 郯城县| 钟山县| 若尔盖县| 綦江县| 乌鲁木齐市| 绥芬河市| 云南省| 普兰县| 藁城市| 怀安县| 扬中市| 德化县| 鄂托克前旗| 绥阳县| 孝感市| 永城市| 泰顺县| 鄂伦春自治旗| 聂拉木县| 三原县| 随州市| 东乌珠穆沁旗| 高邮市| 基隆市| 姚安县| 通州市| 白沙| 独山县| 黄山市| 乌苏市| 奉新县| 丰县| 应用必备|