国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

原來這就是比 ThreadLocal 更快的玩意

開發 后端
根據傳入構造 FastThreadLocal 生成的唯一 index 可以直接從 Object 數組里面找到下標并且進行替換,這樣一來壓根就不會產生沖突,邏輯很簡單,完美。

[[421387]]

本文轉載自微信公眾號「yes的練級攻略」,作者是Yes呀。轉載本文請聯系yes的練級攻略公眾號。

你好,我是yes。

繼上一篇之后我把 ThreadLocal 能問的,都寫了,咱們再來盤一盤 FastThreadLocal ,這個算是 ThreadLocal 的進階版,是 Netty 針對 ThreadLocal 自己造的輪子,所以對 ThreadLocal 沒有完全理解的話,建議先看上一篇文章,打個基礎。

那了解 FastThreadLocal 之后呢,對平日的一些優化可能可以提供一些思路,或者面試就能裝個x。

面試官:ThreadLocal 竟然有xxx這個缺點,那怎么優化啊?

你就把 FastThreadLocal 的實現 BB 一遍,這不就穩妥了嘛!

所以,今天我們就來看看 Netty 是如何實現 FastThreadLocal 的,話不多說,本文大綱如下:

  • 數數 ThreadLocal 的缺點。
  • 應該如何針對 ThreadLocal 缺點改進?
  • FastThreadLocal 的原理。
  • FastThreadLocal VS ThreadLocal 的實操。

這篇下來,進階版 ThreadLocal 基本拿下,下篇我會基于這篇做一個延伸,一個比較底層的延伸,屬于絕對裝x的那種,等下看文章你就知道了,我會埋坑的,哈哈。

預告一下,這篇是個長文,源碼也有點多,但是耐心看完肯定會有收獲的。

發車發車!

數數 ThreadLocal 的缺點

看完上篇文章的同學,應該都很清楚了 ThreadLocal 的一個缺點:hash 沖突用的是線性探測法,效率低。

可以看到,圖上顯示的是經過兩個遍歷找到了空位,假設沖突多了,需要遍歷的次數就多了。并且下次 get 的時候,hash 直接命中的位置發現不是要找的 Entry ,于是就接著遍歷向后找,所以說這個效率低。

而像 HashMap 是通過鏈表法來解決沖突,并且為了防止鏈表過長遍歷的開銷變大,在一定條件之后又會轉變成紅黑樹來查找,這樣的解決方案在頻繁沖突的條件下,肯定是優于線性探測法,所以這是一個優化方向。

不過 FastThreadLocal 不是這樣優化的,我們下面再說。

還有一個缺點是 ThreadLocal 使用了 WeakReference 以保證資源可以被釋放,但是這可能會產生一些 Etnry 的 key 為 null,即無用的 Entry 存在。

所以調用 ThreadLocal 的 get 或 set 方法時,會主動清理無用的 Entry,減輕內存泄漏的發生。

這其實等于把清理的開銷弄到了 get 和 set 上,萬一 get 的時候清理的無用 Entry 特別多,那這次 get 相對而言就比較慢了。

還有一個就是內存泄漏的問題了,當然這個問題只存在于用線程池使用的時候,并且上面也提到了 get 和 set 的時候也能清理一些無用的 Key,所以沒有那么的夸張,只要記得用完后調用 ThreadLocal#remove 就不會有內存泄漏的問題了。

大致就這么幾點。

應該如何針對 ThreadLocal 缺點改進

所以怎么改呢?

前面提到 ThreadLocal hash 沖突的線性探測法不好,還有 Entry 的弱引用可能會發生內存泄漏,這些都和 ThreadLocalMap 有關,所以需要搞個新的 map 來替換 ThreadLocalMap。

而這個 ThreadLocalMap 又是 Thread 里面的一個成員變量,這么一看 Thread 也得動一動,但是我們又無法修改 Thread 的代碼,所以配套的還得弄個新的 Thread。

所以我們不僅得弄個新的 ThreadLocal、ThreadLocalMap 還得弄個配套的 Thread 來用上新的 ThreadLocalMap 。

所以如果想改進 ThreadLocal ,就需要動這三個類。

對應到 Netty 的實現就是 FastThreadLocal、InternalThreadLocalMap、FastThreadLocalThread

然后發散一下思維,既然 Hash 沖突的想線性探測效果不好,你可能比較容易想到的就是上面提到的鏈表法,然后再基于鏈表法說個改成紅黑樹,這個確實是一方面,但是可以再想想。

比如,讓 Hash 不沖突,所以設計一個不會沖突的 hash 算法?不存在的!

所以怎么樣才不會產生沖突呢?

各自取號入座

什么意思?就是每往 InternalThreadLocalMap 中塞入一個新的 FastThreadLocal 對象,就給這個對象發個唯一的下標,然后讓這個對象記住這個下標,到時候去 InternalThreadLocalMap 找 value 的時候,直接通過下標去取對應的 value 。

這樣不就不會沖突了?

這就是 FastThreadLocal 給出的方案,具體下面分析。

還有個內存泄漏的問題,這個其實只要規范的使用即用完后 remove 就好了,其實也沒太好的解決方案,不過 FastThreadLocal 曲線救國了一下,這個也且看下面的分析!

FastThreadLocal 的原理

以下 Netty 基于 4.1 版本分析

先來看下 FastThreadLocal 的定義:

可以看到有個叫 variablesToRemoveIndex 的類成員,并且用 final 修飾的,所以等于每個 FastThreadLocal 都有個共同的不可變 int 值,值為多少等下分析。

然后看到這個 index 沒,在 FastThreadLocal 構造的時候就被賦值了,且也被 final 修飾,所以也不可變,這個 index 就是我上面說的給每個新 FastThreadLocal 都發個唯一的下標,這樣每個 index 就都知道自己的位置了。

上面兩個 index 都是通過 InternalThreadLocalMap.nextVariableIndex() 賦值的,盲猜一下,這個肯定是用原子類遞增實現的。

我們來看一下實現:

確實,在 InternalThreadLocalMap 也定義了一個靜態原子類,每次調用 nextVariableIndex 就返回且遞增,沒有什么別的賦值操作,從這里也可以得知 variablesToRemoveIndex 的值為 0,因為它屬于常量賦值,第一次調用時 nextIndex 的值為 0 。

看到這,不知道大家是否已經感覺到一絲不對勁了。好像有點浪費空間的意思,我們繼續往下看。

InternalThreadLocalMap 對標的就是之前的 ThreadLocalMap 也就是 ThreadLocal 缺點集中的類,需要重點看下。

我們再來回顧一下 ThreadLocalMap 的定義。

它是個 Entry 數組,然后 Entry 里面弱引用了 ThreadLocal 作為 Key。

而 InternalThreadLocalMap 有點不太一樣:

可以看到, InternalThreadLocalMap 好像放棄了 map 的形式,沒用定義 key 和 value,而是一個 Object 數組?

那它是如何通過 Object 來存儲 FastThreadLocal 和對應的 value 的呢?我們從 FastThreadLocal#set 開始分析:

因為我們已經熟悉 ThreadLocal 的套路,所以我們知道 InternalThreadLocalMap 肯定是 FastThreadLocalThread 里面的一個變量。

然后我們從對應的 FastThreadLocalThread 里面拿到了 map 之后,就要執行塞入操作即 setKnownNotUnset。

我們先看一下塞入操作里面的 setIndexedVariable 方法:

可以看到,根據傳入構造 FastThreadLocal 生成的唯一 index 可以直接從 Object 數組里面找到下標并且進行替換,這樣一來壓根就不會產生沖突,邏輯很簡單,完美。

那如果塞入的 value 不是 UNSET(默認值),則執行 addToVariablesToRemove 方法,這個方法又有什么用呢?

是不是看著有點奇怪?這是啥操作?別急,看我畫個圖來解釋解釋:

這就是 Object 數組的核心關系圖了,第一個位置放了一個 set ,set 里面存儲了所有使用的 FastThreadLocal 對象,然后數組后面的位置都放 value。

那為什么要放一個 set 保存所有使用的 FastThreadLocal 對象?

用于刪除,你想想看,假設現在要清空線程里面的所有 FastThreadLocal ,那必然得有一個地方來存放這些 FastThreadLocal 對象,這樣才能找到這些家伙,然后干掉。

所以剛好就把數組的第一個位置騰出來放一個 set 來保存這些 FastThreadLocal 對象,如果要刪除全部 FastThreadLocal 對象的時候,只需要遍歷這個 set ,得到 FastThreadLocal 的 index 找到數組對應的 位置將 value 置空,然后把 FastThreadLocal 從 set 中移除即可。

剛好 FastThreadLocal 里面實現了這個方法,我們來看下:

圖片內容可能有點多了,我們做下小結,理一理上面說的:

首先 InternalThreadLocalMap 沒有采用 ThreadLocalMap k-v形式的存儲方式,而是用 Object 數組來存儲 FastThreadLocal 對象和其 value,具體是在第一個位置存放了一個包含所使用的 FastThreadLocal 對象的 set,然后后面存儲所有的 value。

之所以需要個 set 是為了存儲所有使用的 FastThreadLocal 對象,這樣就能找到這些對象,便于后面的刪除工作。

之所以數組其他位置可以直接存儲 value ,是因為每個 FastThreadLocal 構造的時候已經被分配了一個唯一的下標,這個下標對應的就是 value 所處的下標。

看到這里,不知道大家是否有感受到空間的浪費?

我舉個例子。

假設系統里面一個 new 了 100 個 FastThreadLocal ,那第 100 個 FastThreadLocal 的下標就是 100 ,這個應該沒有疑義。

從上面的 set 方法可以得知,只有調用 set 的時候,才會從當前線程中拿出 InternalThreadLocalMap ,然后往這個 map 的數組里面塞入 value,這里我們再回顧一下 set 的方法。

那這里是什么意思呢?

如果我這個線程之前都沒塞過 FastThreadLocal ,此時要塞入第一個 FastThreadLocal ,構造出來的數組長度是32,但是這個 FastThreadLocal 的下標已經漲到了 100 了,所以這個線程第一次塞值,也僅僅只有這么一個值,數組就需要擴容。

看到沒,這就是我所說的浪費,空間被浪費了。

Netty 相關實現者知道這樣會浪費空間,所以數組的擴容是基于 index 而不是原先數組的大小,你看看如果是基于原先數組的擴容,那么第一次擴容 2 倍,32 變成 64,還是塞不下下標 100 的數據,所以還得擴容一次,這就不美了。

所以可以看到擴容傳進去的參數是 index 。

可以看到,直接基于 index 的向上 2 次冪取整。然后就是擴容的拷貝,這里是直接進行數組拷貝,不需要進行 rehash,而 ThreadLocalMap 的擴容需要進行rehash,也就是重新基于 key 的 hash 值進行位置的分配,所以這個也是 FastThreadLocal 優于ThreadLocal 的一個點。

對了,上面那個向上 2 次冪取整的操作,不知道你們熟悉不熟悉,這個和 HashMap 的實現是一致的。

咳咳,但是我沒有證據,只能說優秀的代碼,就是源遠流長。

所以從上面的實現可以得知 Netty 就是特意這樣設計的,用多余的空間去換取不會沖突的 set 和 get ,這樣寫入和獲取的速度就更快了,這就是典型的空間換時間。

好了,想必此時你已經弄懂了 FastThreadLocal 的核心原理了,我們再來看看 get 方法的實現,我想你應該能腦補這個實現了。

是吧,沒啥難度,index 就是 FastThreadLocal 構造時候預先分配好的那個下標,然后直接進行一個數組下標查找,如果沒找到就調用 init 方法進行初始化。

我們這里再繼續探究一下InternalThreadLocalMap.get(),這里面做了一個兼容。不過我要先介紹一下 FastThreadLocalThread ,就是這玩意替代了 Thread。

可以看到它繼承了 Thread ,并且弄了一個成員變量就是我們前面說的 InternalThreadLocalMap。

然后我們再來看一下 get 方法,我截了好幾個,不過邏輯很簡單。

這里之所以分了 fastGet 和 slowGet 是為了做一個兼容,假設有個不熟悉的人,他用了 FastThreadLocal 但是沒有配套使用 FastThreadLocalThread ,然后調用 FastThreadLocal#get 的時候去 Thread 里面找 InternalThreadLocalMap 那不就傻了嗎,會報錯的。

所以就再弄了個 slowThreadLocalMap ,它是個 ThreadLocal ,里面保存 InternalThreadLocalMap 來兼容一下這個情況。

從這里我們也能得知,FastThreadLocal 最好和 FastThreadLocalThread 配套使用,不然就隔了一層了。

  1. FastThreadLocal<String> threadLocal = new FastThreadLocal<String>(); 
  2. Thread t = new FastThreadLocalThread(new Runnable() { //記得要 new FastThreadLocalThread 
  3.      public void run() { 
  4.       threadLocal.get(); 
  5.       .... 
  6.      } 
  7.  }); 

好了,get 和 set 這兩個核心操作都分析完了,我們最后再來看一下 remove 操作吧。

很簡單對吧,把數組里的 value 給覆蓋了,然后再到 set 里把對應的 FastThreadLocal 對象給刪了。

不過看到這里,可能有人會發出疑惑,內存泄漏相關的點呢?

其實吧,可以看到 FastThreadLocal 就沒用弱引用,所以它把無用 FastThreadLocal 的清理就寄托到規范使用上,即沒用了就主動調用 remove 方法。

但是它曲線救國了一下,我們來看一下 FastThreadLocalRunnable 這個類:

我已經把重點畫出來了,可以看到這個 Runnable 執行完畢之后,會主動調用 FastThreadLocal.removeAll() 來清理所有的 FastThreadLocal,這就是我說的曲線救國,怕你完了調用 remove ,沒事我幫你封裝一下,就是這么貼心。

當然,這個前提是你不能用 Runnable 而是用 FastThreadLocalRunnable。不過這里 Netty 也是做了封裝的。

Netty 實現了一個 DefaultThreadFactory 工廠類來創建線程。

你看,你傳入 Runnable 是吧,沒事,我把它包成 FastThreadLocalRunnable,并且我 new 回去的線程是 FastThreadLocalThread 類型,這樣就能在很大程度上避免使用的錯誤,也減少了使用的難度。

這也是工廠方法這個設計模式的好處之一啦。所以工程上如果怕對方沒用對,我們就封裝了再給別人使用,這樣也屏蔽了一些細節,他好你也好。

所以說多看看開源框架的源碼,有很多可以學習的地方!好了,FastThreadLocal 原理大致就說到這里。

FastThreadLocal VS ThreadLocal

到此,我們已經充分了解了兩者之間的不同,但是 Fast 到底有多 Fast 呢?

我們用實驗說話,Netty 源碼里面已經有 benchmark 了,我們直接跑就行了

里面有兩個實驗:

FastPath 對應的是使用 FastThreadLocalThread 線程對象。

SlowPath 對應的是使用 Thread 線程對象。

兩個實驗都是分別定義了 ThreadLocal 和 FastThreadLocal :

我們來看一下執行的結果:

FastPath:

SlowPath:

可以看到搭配 FastThreadLocalThread 來使用 FastThreadLocal 吞吐確實比使用 ThreadLocal 大,但是好像也沒大太多?

不過,我在網上有看別比人的 benchmark 對比,同樣的代碼,他的結果是大了三倍。

我反正又跑了幾遍,每次都比原生的 ThreadLocal 吞吐好,但是也沒好那么多...有點奇怪。

至于 FastThreadLocal 搭配 Thread 則吞吐比 ThreadLocal 都少,說明 FastThreadLocal 的使用必須得搭配 FastThreadLocalThread ,不然就是反向優化了。

代碼在 netty 的 microbench 這個項目里,有興趣的可以自己 down 下來跑一跑看看。

最后

我們再來總結一下:

  • FastThreadLocal 通過分配下標直接定位 value ,不會有 hash 沖突,效率較高。
  • FastThreadLocal 采用空間換時間的方式來提高效率。
  • FastThreadLocal 需要配套 FastThreadLocalThread 使用,不然還不如原生 ThreadLocal。
  • FastThreadLocal 使用最好配套 FastThreadLocalRunnable,這樣執行完任務后會主動調用 removeAll 來移除所有 FastThreadLocal ,防止內存泄漏。
  • FastThreadLocal 的使用也是推薦用完之后,主動調用 remove。

這就是 Netty 實現的加強版 ThreadLocal,如果你看過 Netty 源碼,你會發現內部是有挺多使用 ThreadLocal 的場景,所以這個優化還是有必要的。

并且 Netty work 線程池默認線程數是兩倍 CPU 核心數,所以線程不會太多,那么空間的浪費其實也不會很多,所以這波空間換時間影響不大。

好了,文章就到這了。挖個坑,我在 InternalThreadLocalMap 這個類里面發現了一些奇怪的 long 變量。

懂行的同學看著可能知道,這是為了填充 Cache Line,避免偽共享問題的產生。

ok ,那為什么被標記了@deprecated?并且說將來的版本要被移除?

且聽下回分解。

 

責任編輯:武曉燕 來源: yes的練級攻略
相關推薦

2021-06-15 11:16:49

代碼耦合開發

2020-07-17 19:31:19

PythonR編程

2018-11-08 15:30:04

JavaScriptES6異步

2015-07-21 10:24:02

Windows RT升級

2014-01-02 14:04:42

2019-01-02 04:40:19

物聯網企業IOT

2024-12-13 16:37:56

SpringBootJava

2016-01-12 17:01:45

Bootstrap原因

2015-07-27 10:56:02

2020-02-17 15:55:22

Office 365

2024-04-24 09:47:36

2018-11-01 13:38:51

Java中斷停止

2015-01-09 10:10:00

Linux

2015-09-19 13:45:27

2022-11-21 16:10:31

奔馳可靠性排名

2025-09-01 08:12:37

JavaScrip框架DOM

2023-07-13 09:00:00

人工智能GPT模型

2021-08-17 13:31:11

加密貨幣區塊鏈貨幣

2019-11-27 08:52:13

網易裁員微博

2021-01-13 10:51:08

PromissetTimeout(函數
點贊
收藏

51CTO技術棧公眾號

日韩精品一区二区三区swag| 人人澡人人澡人人看欧美| av动漫在线免费观看| 精品av一区二区| 亚洲国产精久久久久久久| av网站免费在线| 久久久成人网| 欧洲日韩成人av| 新片速递亚洲合集欧美合集| 日韩欧美在线一区| 日本黄网站免费| 久久精品久久久精品美女| 亚洲va久久久噜噜噜久久天堂| 日本精品在线观看| 日韩风俗一区 二区| 免费福利在线视频| 国产亚洲综合av| 欧美在线观看视频免费| 国产视频欧美| 91精品天堂| 国产最新精品| 色综合久久久久久中文网| 成人性生交大片免费看在线播放| 欧美日韩国产黄| 怡红院亚洲色图| 成人av动漫在线| 日韩国产高清一区| 日韩视频一区| 99久久精品久久久久久ai换脸| 欧美日韩直播| 欧美精品激情在线观看| 高清在线一区| 亚洲精品国产精品乱码不99按摩| 国产区视频在线| 亚洲在线成人精品| 日本一二区视频| 亚洲美女区一区| 日本免费专区| 亚洲自拍另类综合| 中文字幕免费在线| 午夜影院在线观看欧美| 日本视频一二区| 亚洲制服丝袜一区| 中文字幕一二三区在线观看| 一区二区三区不卡视频| 中文字幕日产av一二三区| 亚洲精品国产精华液| 成年美女网站| 亚洲国产wwwccc36天堂| 天海翼一区二区三区免费| 亚洲美女屁股眼交| 永久www成人看片| 亚洲自拍与偷拍| 桃花色综合影院| 欧美剧情电影在线观看完整版免费励志电影 | 国产高清www| caoporn国产精品| 国产午夜大地久久| 91美女在线观看| 99视频免费| 亚洲国产人成综合网站| 97在线资源| 在线观看亚洲成人| 怡红院av在线| 精品一区二区电影| 国产一区二区三区| 国产成人精品日本亚洲专区61| 日本黄色精品| 高清视频一区二区三区| 免费观看成人av| 日韩 欧美 高清| 亚洲精品精品亚洲| 久久综合之合合综合久久| 亚洲精选在线观看| 给我免费播放日韩视频| 亚洲va男人天堂| 国内一区二区在线| 超碰在线97免费| 欧美日韩中文在线| 在线视频cao| 69影院欧美专区视频| 91成人精品| 亚洲av首页在线| 一区二区三区中文字幕电影 | 97久久久精品综合88久久| **三级三级97片毛片| 欧美三级中文字幕在线观看| 二区三区不卡| 国产91在线播放精品91| 玖玖国产精品视频| 99色在线视频| 日韩精品一区二区三区视频 | 日韩理论片网站| 欧美69xxxx| 欧美激情免费视频| 欧美专区一区二区三区| 成人午夜激情av| 欧美成人三级在线| 国产精品嫩模av在线| 欧美日韩免费精品| 亚洲欧洲国产专区| 国产v日韩v欧美v| 国产精品美女在线| 成人免费av网站| 国产天堂在线| 久久国产精彩视频| 久久国产精品久久w女人spa| 免费全黄无遮挡裸体毛片| 日韩成人在线视频| 久久久久美女| 欧美成人精品欧美一级乱| 欧美精品少妇一区二区三区| 日韩免费成人| 天堂va久久久噜噜噜久久va| 亚洲伊人色欲综合网| 视频精品导航| 日本成人黄色| 欧美日在线观看| 鲁大师精品99久久久| 午夜在线视频免费观看| 欧洲精品一区二区| 亚洲国产精品嫩草影院久久av| 异国色恋浪漫潭| 777奇米成人网| 亚洲精品网址| 久草在线在线视频| 久久久精品在线| 国产一区二区三区香蕉| 欧美性猛交xxx乱大交3蜜桃| 91精品国产综合久久久久久蜜臀 | 日韩一区二区免费视频| 大胆日韩av| 91原色影院| 中文字幕亚洲一区在线观看 | 97人人模人人爽人人少妇| 国产亚洲短视频| 裤袜国产欧美精品一区| 国产三区二区一区久久| 亚洲成在人线在线播放| 亚洲日本三级| 成人黄色免费电影| 欧美美最猛性xxxxxx| 成人黄色国产精品网站大全在线免费观看 | 九九久久电影| 免费在线激情视频| 亚洲欧洲自拍偷拍| 奇米精品一区二区三区在线观看 | 亚洲成a天堂v人片| 国产欧美88| 国产自产精品| 日韩欧美在线免费观看| 日韩www.| 欧美在线观看在线观看| 成人午夜激情免费视频| 五月天网站亚洲| 欧美电影一区| 美国成人av| **亚洲第一综合导航网站| 欧美性xxxx极品hd欧美风情| 日韩av密桃| 日本中文字幕一区二区有码在线| 国产精品夜间视频香蕉| 欧美色欧美亚洲高清在线视频| 香蕉久久网站| 在线免费观看黄色| 奇米影视首页 狠狠色丁香婷婷久久综合 | 亚洲大胆人体视频| 九九精品视频在线看| 最新中文字幕在线播放| 妞干网在线播放| 欧美黄色三级网站| 亚洲日本欧美天堂| 亚洲精品小说| 日本中文字幕中出在线| 300部国产真实乱| 欧美成人午夜激情| 亚洲激情男女视频| 国产精品观看| 天堂√中文最新版在线| 免费在线激情视频| 国产精品久久久久免费a∨| 欧美亚洲国产一区在线观看网站| 日韩av二区在线播放| 婷婷久久综合九色综合99蜜桃| 九色丨porny丨自拍入口| 91久久精品日日躁夜夜躁国产| 欧美日韩国产乱码电影| 国产麻豆日韩欧美久久| 粉嫩av一区二区| 黄色在线小视频| 亚洲制服欧美久久| 国内成人精品一区| 色老综合老女人久久久| 国精产品一区一区三区mba视频 | 欧美在线日韩精品| 中文字幕亚洲一区二区三区五十路| 成人免费在线视频观看| 在线视频日韩| 久久伊人久久| 国产资源在线看|