一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java高進進階之FastThreadLocal源碼詳解(修復ThreadLocal的缺陷)

Java高進進階之FastThreadLocal源碼詳解(修復ThreadLocal的缺陷)

2021-09-14 01:07Android開發編程 Java教程

在Netty中,要使用 FastThreadLocal 實現線程本地變量需要將線程包裝成 FastThreadLocalThread ,如果不是 FastThreadLocalThread ,會使用 slowThreadLocalMap的 ThreadLocal 來存儲變量副本。

Java高進進階之FastThreadLocal源碼詳解(修復ThreadLocal的缺陷)

前言

ThreadLocal被ThreadLocalMap中的entry的key弱引用,如果出現GC的情況時,

沒有被其他對象引用,會被回收,但是ThreadLocal對應的value卻不會回收,容易造成內存泄漏,這也間接導致了內存溢出以及數據假丟失;

那么問題來了,有沒有更高效的ThreadLocal有;

今天我們就來分析一波FastThreadLocalThread

一、FastThreadLocalThread源碼分析

Netty為了在某些場景下提高性能,改進了jdk ThreadLocal,Netty實現的FastThreadLocal 優化了Java 原生 ThreadLocal 的訪問速度,存儲速度。避免了檢測弱引用帶來的 value 回收難問題,和數組位置沖突帶來的線性查找問題,解決這些問題并不是沒有代價;

Netty實現的 FastThreadLocal 底層也是通過數組存儲 value 對象,與Java原生ThreadLocal使用自身作為Entry的key不同,FastThreadLocal通過保存數組的全局唯一下標,實現了對value的快速訪問。同時FastThreadLocal 也實現了清理對象的方法;

1、FastThreadLocalThread

在Netty中,要使用 FastThreadLocal 實現線程本地變量需要將線程包裝成 FastThreadLocalThread ,如果不是 FastThreadLocalThread ,會使用 slowThreadLocalMap的 ThreadLocal 來存儲變量副本;

  1. io.netty.util.concurrent.DefaultThreadFactory 
  2. @Override 
  3. public Thread newThread(Runnable r) { 
  4.     Thread t = newThread(FastThreadLocalRunnable.wrap(r), prefix + nextId.incrementAndGet()); 
  5.     // 一般daemon為false,意思是不設置為守護線程 
  6.     if (t.isDaemon() != daemon) { 
  7.         t.setDaemon(daemon); 
  8.     } 
  9.     // 優先級 默認為5 
  10.     if (t.getPriority() != priority) { 
  11.         t.setPriority(priority); 
  12.     } 
  13.     return t; 
  14. protected Thread newThread(Runnable r, String name) { 
  15.     return new FastThreadLocalThread(threadGroup, r, name); 

FastThreadLocalThread 繼承自Thread類,有如下成員變量:

  1. io.netty.util.concurrent.FastThreadLocalThread 
  2. // 任務執行完,是否清除FastThreadLocal的標記 
  3. private final boolean cleanupFastThreadLocals; 
  4. // 類似于Thread類中ThreadLocalMap,為了實現FastThreadLocal 
  5. private InternalThreadLocalMap threadLocalMap; 

2、 InternalThreadLocalMap

Java高進進階之FastThreadLocal源碼詳解(修復ThreadLocal的缺陷)

FastThreadLocalThread.threadLocalMap 是 InternalThreadLocalMap 對象實例。在第一次獲取FTL數據時,會初始化FastThreadLocalThread.threadLocalMap,調用的構造函數如下:

  1. private InternalThreadLocalMap() { 
  2.             //為了簡便,InternalThreadLocalMap父類 
  3.             //UnpaddedInternalThreadLocalMap不展開介紹 
  4.             super(newIndexedVariableTable()); 
  5.         } 
  6.         //默認的數組大小為32,且使用UNSET對象填充數組 
  7.         //如果下標處數據為UNSET,則表示沒有數據 
  8.         private static Object[] newIndexedVariableTable() { 
  9.             Object[] array = new Object[32]; 
  10.             Arrays.fill(array, UNSET); 
  11.             return array; 
  12.         } 

為了避免寫時候影響同一cpu緩沖行的其他數據并發訪問,其使用了緩存行填充技術 (cpu 緩沖行填充),在類定義中聲明了如下long字段進行填充;

  1. //InternalThreadLocalMap 
  2. // Cache line padding (must be public)  
  3. // With CompressedOops enabled, an instance of this class should occupy at least 128 bytes.  
  4. public long rp1, rp2, rp3, rp4, rp5, rp6, rp7, rp8, rp9; 

FTL使用的數組下標是InternalThreadLocalMap中的靜態變量nextIndex統一遞增生成的:

  1. static final AtomicInteger nextIndex = new AtomicInteger(); 
  2. public static int nextVariableIndex() { 
  3.     //Netty中所有FTL數組下標都是通過遞增這個靜態變量實現的 
  4.     //采用靜態變量生成所有FTL元素在數組中的下標會造成一個問題, 
  5.     //會造成InternalThreadLocalMap中數組不必要的自動擴容 
  6.     int index = nextIndex.getAndIncrement(); 
  7.     if (index < 0) { 
  8.         nextIndex.decrementAndGet(); 
  9.         throw new IllegalStateException("too many thread-local indexed variables"); 
  10.     } 
  11.     return index

InternalThreadLocalMap.nextVariableIndex()方法獲取FTL在該FastThreadLocalThread.threadLocalMap數組下標,因為InternalThreadLocalMap.nextVariableIndex() 使用靜態域 nextIndex 遞增維護所有FTL的下標,會造成后面實例化的 FTL 下標過大,如果FTL下標大于其對應 FastThreadLocalThread.threadLocalMap 數組的長度,會進行數組的自動擴容,如下:

  1. private void expandIndexedVariableTableAndSet(int index, Object value) { 
  2.     Object[] oldArray = indexedVariables; 
  3.     final int oldCapacity = oldArray.length; 
  4.     //下面復雜的實現是為了將newCapacity規范為最接近的一個2的指數,  
  5.     //這段代碼在早期的 jdk HashMap 中見過 
  6.     int newCapacity = index
  7.     newCapacity |= newCapacity >>>  1; 
  8.     newCapacity |= newCapacity >>>  2; 
  9.     newCapacity |= newCapacity >>>  4; 
  10.     newCapacity |= newCapacity >>>  8; 
  11.     newCapacity |= newCapacity >>> 16; 
  12.     newCapacity ++; 
  13.     Object[] newArray = Arrays.copyOf(oldArray, newCapacity); 
  14.     Arrays.fill(newArray, oldCapacity, newArray.length, UNSET); 
  15.     newArray[index] = value; 
  16.     indexedVariables = newArray; 

3、FastThreadLocal

構造函數:

有兩個重要的下標域,FTL不僅在FastThreadLocalThread.threadLocalMap中保存了用戶實際使用的value(在數組中的下標為index),還在數組中保存為了實現清理記錄的相關數據,也即下標variablesToRemoveIndex,一般情況 variablesToRemoveIndex = 0;因為variablesToRemoveIndex 是靜態變量,所以全局唯一;

  1. //如果在該FTL中放入了數據,也就實際調用了其set或get函數,會在 
  2.        //該FastThreadLocalThread.threadLocalMap數組的 
  3.        // variablesToRemoveIndex下標處放置一個IdentityHashMap, 
  4.        //并將該FTL放入IdentityHashMap中,在后續清理時會取出 
  5.        //variablesToRemoveIndex下標處的IdentityHashMap進行清理 
  6.         private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex(); 
  7.        //在threadLocalMap數組中存放實際數據的下標 
  8.         private final int index
  9.         public FastThreadLocal() { 
  10.             index = InternalThreadLocalMap.nextVariableIndex(); 
  11.         } 

用戶可擴展的函數:

  1. //初始化 value 函數 
  2. protected V initialValue() throws Exception { 
  3.     return null
  4. //讓使用者在該FTL被移除時可以有機會做些操作。 
  5. protected void onRemoval(@SuppressWarnings("UnusedParameters") V value) throws Exception { } 

FastThreadLocalThread

Java高進進階之FastThreadLocal源碼詳解(修復ThreadLocal的缺陷)

cleanupFastThreadLocals 字段在 4.1 的最新版本中已經沒有在用到了

  1. /** 
  2.  * true,表示FTL會在線程結束時被主動清理 見  FastThreadLocalRunnable 類 
  3.  * false,需要將FTL放入后臺清理線程的隊列中 
  4.  */ 
  5. // This will be set to true if we have a chance to wrap the Runnable. 
  6. //這個字段則用于標識該線程在結束時是否會主動清理FTL 
  7. private final boolean cleanupFastThreadLocals; 
  8. //次對象將在 第一次 FastThreadLocal.get 和 FastThreadLocal.set 時候創建 
  9. private InternalThreadLocalMap threadLocalMap; 
  10. public FastThreadLocalThread(Runnable target) { 
  11.     super(FastThreadLocalRunnable.wrap(target)); 
  12.     cleanupFastThreadLocals = true

4、 set 方法

  1. public final void set(V value) { 
  2.     //判斷設置的 value 值是否是缺省值 
  3.     if (value != InternalThreadLocalMap.UNSET) { 
  4.         //獲取當前線程的 InternalThreadLocalMap , 如果當前線程為FastThreadLocalThread,那么直接通過threadLocalMap引用獲取 
  5.         //否則通過 jdk 原生的 threadLocal 獲取 
  6.         InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); 
  7.         //FastThreadLocal 對應的 index 下標的 value 替換成新的 value 
  8.         setKnownNotUnset(threadLocalMap, value); 
  9.     } else { 
  10.         //如果放置的對象為UNSET,則表示清理,會對該FTL進行清理,類似毒丸對象 
  11.         remove(); 
  12.     } 

這里擴容方會調用 InternalThreadLocalMap.expandIndexedVariableTableAndSet

  1. private void setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) { 
  2.     //在數組下標index處放置實際對象,如果index大于數組length,會進行數組擴容. 
  3.     if (threadLocalMap.setIndexedVariable(index, value)) { 
  4.         //放置成功之后,將該FTL加入到 variablesToRemoveIndex 下標的 
  5.         //IdentityHashMap,等待后續清理 
  6.         addToVariablesToRemove(threadLocalMap, this); 
  7.     } 
  1. /** 
  2.  * 該FTL加入到variablesToRemoveIndex下標的IdentityHashMap 
  3.  * IdentityHashMap的特性可以保證同一個實例不會被多次加入到該位置 
  4.  */ 
  5. @SuppressWarnings("unchecked"
  6. private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) { 
  7.     //獲取 variablesToRemoveIndex下標處的 IdentityHashMap 
  8.     Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex); 
  9.     Set<FastThreadLocal<?>> variablesToRemove; 
  10.     //如果是第一次獲取,則 variablesToRemoveIndex下標處的值為 UNSET 
  11.     if (v == InternalThreadLocalMap.UNSET || v == null) { 
  12.         //新建一個新的 IdentityHashMap 并 
  13.         variablesToRemove = Collections.newSetFromMap(new IdentityHashMap<FastThreadLocal<?>, Boolean>()); 
  14.         //放入到下標variablesToRemoveIndex處 
  15.         threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove); 
  16.     } else { 
  17.         variablesToRemove = (Set<FastThreadLocal<?>>) v; 
  18.     } 
  19.     //將該FTL放入該IdentityHashMap中 
  20.     variablesToRemove.add(variable); 

下面看InternalThreadLocalMap.get()實現:

  1. public static InternalThreadLocalMap get() { 
  2.     Thread thread = Thread.currentThread(); 
  3.     //首先看當前 thread 是否為FastThreadLocalThread實例 
  4.     //如果是的話,可以快速通過引用,獲取到其 threadLocalMap 
  5.     if (thread instanceof FastThreadLocalThread) { 
  6.         return fastGet((FastThreadLocalThread) thread); 
  7.     } else { 
  8.         //如果不是,則 jdk 原生慢速獲取到其 threadLocalMap 
  9.         return slowGet(); 
  10.     } 

5、 get 方法

get方法極為簡單,實現如下:

  1. ===========================FastThreadLocal========================== 
  2. public final V get() { 
  3.         InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get(); 
  4.         Object v = threadLocalMap.indexedVariable(index); 
  5.         if (v != InternalThreadLocalMap.UNSET) { 
  6.             return (V) v; 
  7.         } 
  8.         return initialize(threadLocalMap); 
  9.     } 

首先獲取當前線程的map,然后根據 FastThreadLocal的index 獲取value,然后返回,如果是空對象,則通過 initialize 返回,initialize 方法會將返回值設置到 map 的槽位中,并放進 Set 中;

  1. initialize 
  2. ============================FastThreadLocal========================== 
  3. private V initialize(InternalThreadLocalMap threadLocalMap) { 
  4.     V v = null
  5.     try { 
  6.         //1、獲取初始值 
  7.         v = initialValue(); 
  8.     } catch (Exception e) { 
  9.         throw new RuntimeException(e); 
  10.     } 
  11.     // 2、設置value到InternalThreadLocalMap中 
  12.     threadLocalMap.setIndexedVariables(index, v); 
  13.     // 3、添加當前的FastThreadLocal到InternalThreadLocalMap的Set<FastThreadLocal<?>>中 
  14.     addToVariablesToRemove(threadLocalMap, this); 
  15.     return v; 
  16. //初始化參數:由子類復寫 
  17. protected V initialValue() throws Exception { 
  18.     return null
  • 獲取 ThreadLocalMap
  • 直接通過索引取出對象
  • 如果為空那么調用初始化方法初始化

6、ftl的資源回收機制

netty中ftl的兩種回收機制回收機制:

自動:使用ftlt執行一個被FastThreadLocalRunnable wrap的Runnable任務,在任務執行完畢后會自動進行ftl的清理;

手動:ftl和InternalThreadLocalMap都提供了remove方法,在合適的時候用戶可以(有的時候也是必須,例如普通線程的線程池使用ftl)手動進行調用,進行顯示刪除;

  1. FastThreadLocalRunnable 
  2. final class FastThreadLocalRunnable implements Runnable { 
  3.     private final Runnable runnable; 
  4.     @Override 
  5.     public void run() { 
  6.         try { 
  7.             runnable.run(); 
  8.         } finally { 
  9.             FastThreadLocal.removeAll(); 
  10.         } 
  11.     } 
  12.     static Runnable wrap(Runnable runnable) { 
  13.         return runnable instanceof FastThreadLocalRunnable  
  14.                 ? runnable : new FastThreadLocalRunnable(runnable); 
  15.     } 

如果將線程執行的任務包裝成 FastThreadLocalRunnable,那么在任務執行完后自動刪除ftl的資源。

  1. ===============================FastThreadLocal=========================== 
  2. public static void removeAll() { 
  3.     // 獲取到map 
  4.     InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet(); 
  5.     if (threadLocalMap == null) { 
  6.         return
  7.     } 
  8.     try { 
  9.         // 獲取到Set<FastThreadLocal>集合 
  10.         Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex); 
  11.         if (v != null && v != InternalThreadLocalMap.UNSET) { 
  12.             @SuppressWarnings("unchecked"
  13.             Set<FastThreadLocal<?>> variablesToRemove = (Set<FastThreadLocal<?>>) v; 
  14.             // 將Set轉換為數組 
  15.             FastThreadLocal<?>[] variablesToRemoveArray = 
  16.                     variablesToRemove.toArray(new FastThreadLocal[variablesToRemove.size()]); 
  17.             // 遍歷數組,刪除每一個FastThreadLocal對應的value 
  18.             for (FastThreadLocal<?> tlv: variablesToRemoveArray) { 
  19.                 tlv.remove(threadLocalMap); 
  20.             } 
  21.         } 
  22.     } finally { 
  23.         // 刪除當前線程的InternalThreadLocalMap 
  24.         InternalThreadLocalMap.remove(); 
  25.     } 
  26. public static void remove() { 
  27.     Thread thread = Thread.currentThread(); 
  28.     if (thread instanceof FastThreadLocalThread) { 
  29.          // 將FastThreadLocalThread 內部的map置位null 
  30.         ((FastThreadLocalThread) thread).setThreadLocalMap(null); 
  31.     } else { 
  32.         // 將 ThreadLocal內部ThreadLocalMap 中的value置位null 
  33.         slowThreadLocalMap.remove(); 
  34.     } 

remove方法:

  1. ===============================FastThreadLocal========================== 
  2. private void remove() { 
  3.     remove(InternalThreadLocalMap.getIfSet()); 
  4. private void remove(InternalThreadLocalMap threadLocalMap) { 
  5.     if (threadLocalMap == null) { 
  6.         return
  7.     } 
  8.     // 從 InternalThreadLocalMap 中刪除當前的FastThreadLocal對應的value并設UNSET 
  9.     Object v = threadLocalMap.removeIndexedVariable(index); 
  10.     // 從 InternalThreadLocalMap 中的Set<FastThreadLocal<?>>中刪除當前的FastThreadLocal對象 
  11.     removeFromVariablesToRemove(threadLocalMap, this); 
  12.     // 如果刪除的是有效值,則進行onRemove方法的回調 
  13.     if (v != InternalThreadLocalMap.UNSET) { 
  14.         try { 
  15.             // 回調子類復寫的onRemoved方法,默認為空實現 
  16.             onRemoved((V) v); 
  17.         } catch (Exception e) { 
  18.             throw new RuntimeException(e); 
  19.         } 
  20.     } 

總結

只有不斷的學習,不斷的找到自己的缺點才可以進步;

一起加油;

原文地址:https://mp.weixin.qq.com/s/WNfFAgnXHyBSbCGwo1pyJQ

延伸 · 閱讀

精彩推薦
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    這篇文章主要介紹了Java使用SAX解析xml的示例,幫助大家更好的理解和學習使用Java,感興趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java實現搶紅包功能

    Java實現搶紅包功能

    這篇文章主要為大家詳細介紹了Java實現搶紅包功能,采用多線程模擬多人同時搶紅包,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙...

    littleschemer13532021-05-16
  • Java教程升級IDEA后Lombok不能使用的解決方法

    升級IDEA后Lombok不能使用的解決方法

    最近看到提示IDEA提示升級,尋思已經有好久沒有升過級了。升級完畢重啟之后,突然發現好多錯誤,本文就來介紹一下如何解決,感興趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程xml與Java對象的轉換詳解

    xml與Java對象的轉換詳解

    這篇文章主要介紹了xml與Java對象的轉換詳解的相關資料,需要的朋友可以參考下...

    Java教程網2942020-09-17
  • Java教程小米推送Java代碼

    小米推送Java代碼

    今天小編就為大家分享一篇關于小米推送Java代碼,小編覺得內容挺不錯的,現在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧...

    富貴穩中求8032021-07-12
  • Java教程20個非常實用的Java程序代碼片段

    20個非常實用的Java程序代碼片段

    這篇文章主要為大家分享了20個非常實用的Java程序片段,對java開發項目有所幫助,感興趣的小伙伴們可以參考一下 ...

    lijiao5352020-04-06
  • Java教程Java8中Stream使用的一個注意事項

    Java8中Stream使用的一個注意事項

    最近在工作中發現了對于集合操作轉換的神器,java8新特性 stream,但在使用中遇到了一個非常重要的注意點,所以這篇文章主要給大家介紹了關于Java8中S...

    阿杜7472021-02-04
  • Java教程Java BufferWriter寫文件寫不進去或缺失數據的解決

    Java BufferWriter寫文件寫不進去或缺失數據的解決

    這篇文章主要介紹了Java BufferWriter寫文件寫不進去或缺失數據的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望...

    spcoder14552021-10-18
主站蜘蛛池模板: 亚洲精品一区二区三区在线看 | www亚洲国产| tobu8中国在线观看免费视频 | 国产在线拍 | xnxx18美女 | 国产男女爱视频在线观看 | 色悠久久久 | 亚洲精品综合一二三区在线 | 精品午夜久久福利大片免费 | xxx黑人又大粗又长 xxxx性欧美极品另类 | 国产九九热视频 | 国产精品视频久久 | 91se精品免费观看 | 91素人约啪| 日韩欧美国产一区 | 非洲一级毛片又粗又长aaaa | 把老师操了 | 欧美影院一区二区 | 性色AV乱码一区二区三区视频 | 国产视频一二三区 | 九九热视频 这里有精品 | 精品无人乱码一区二区三区 | 美女黄板视频 | sao虎影院桃红视频在线观看 | 日韩高清一区二区 | 精品国产一区二区三区在线 | 成人福利网站含羞草 | 无限资源在线观看完整版免费下载 | 国产精品麻豆久久99 | 国产视频一二三区 | 九九九九九九精品免费 | 久久er99热精品一区二区 | 99午夜高清在线视频在观看 | 亚洲 小说 欧美 激情 另类 | 小鸟酱在线播放 | 欧美视频精品一区二区三区 | 午夜国产精品视频 | 暖暖在线精品日本中文 | 国产99视频精品免费视频7 | 99热在线获取最新地址 | 506rr亚洲欧美|