FastThreadLocal 是什么鬼?吊打 ThreadLocal 的存在??!
ThreadLocal 大家都知道是線程本地變量,今天棧長再介紹一個(gè)神器:FastThreadLocal,從字面上看就是:Fast + ThreadLocal,一個(gè)快的 ThreadLocal?這到底是什么鬼呢?
一、FastThreadLocal 簡(jiǎn)介
FastThreadLocal 并不是 JDK 自帶的,而是在 Netty 中造的一個(gè)輪子,Netty 為什么要重復(fù)造輪子呢?
來看下它源碼中的注釋定義:
- /**
- * A special variant of {@link ThreadLocal} that yields higher access performance when accessed from a
- * {@link FastThreadLocalThread}.
- * <p>
- * Internally, a {@link FastThreadLocal} uses a constant index in an array, instead of using hash code and hash table,
- * to look for a variable. Although seemingly very subtle, it yields slight performance advantage over using a hash
- * table, and it is useful when accessed frequently.
- * </p><p>
- * To take advantage of this thread-local variable, your thread must be a {@link FastThreadLocalThread} or its subtype.
- * By default, all threads created by {@link DefaultThreadFactory} are {@link FastThreadLocalThread} due to this reason.
- * </p><p>
- * Note that the fast path is only possible on threads that extend {@link FastThreadLocalThread}, because it requires
- * a special field to store the necessary state. An access by any other kind of thread falls back to a regular
- * {@link ThreadLocal}.
- * </p>
- *
- * @param <V> the type of the thread-local variable
- * @see ThreadLocal
- */
- public class FastThreadLocal<V> {
- ...
- }
FastThreadLocal 是一個(gè)特殊的 ThreadLocal 變體,當(dāng)從線程類 FastThreadLocalThread 中訪問 FastThreadLocalm時(shí)可以獲得更高的訪問性能。如果你還不知道什么是ThreadLocal,可以關(guān)注公眾號(hào)Java技術(shù)棧閱讀我之前分享的文章。 二、FastThreadLocal 為什么快? 在 FastThreadLocal 內(nèi)部,使用了索引常量代替了 Hash Code 和哈希表,源代碼如下: FastThreadLocal 內(nèi)部維護(hù)了一個(gè)索引常量 index,該常量在每次創(chuàng)建 FastThreadLocal 中都會(huì)自動(dòng)+1,從而保證了下標(biāo)的不重復(fù)性。 這要做雖然會(huì)產(chǎn)生大量的 index,但避免了在 ThreadLocal 中計(jì)算索引下標(biāo)位置以及處理 hash 沖突帶來的損耗,所以在操作數(shù)組時(shí)使用固定下標(biāo)要比使用計(jì)算哈希下標(biāo)有一定的性能優(yōu)勢(shì),特別是在頻繁使用時(shí)會(huì)非常顯著,用空間換時(shí)間,這就是高性能 Netty 的巧妙之處。 要利用 FastThreadLocal 帶來的性能優(yōu)勢(shì),就必須結(jié)合使用 FastThreadLocalThread 線程類或其子類,因?yàn)? FastThreadLocalThread 線程類會(huì)存儲(chǔ)必要的狀態(tài),如果使用了非 FastThreadLocalThread 線程類則會(huì)回到常規(guī) ThreadLocal。 Netty 提供了繼承類和實(shí)現(xiàn)接口的線程類: Netty 也提供了 DefaultThreadFactory 工廠類,所有由 DefaultThreadFactory 工廠類創(chuàng)建的線程默認(rèn)就是 FastThreadLocalThread 類型,來看下它的創(chuàng)建過程: 先創(chuàng)建 FastThreadLocalRunnable,再創(chuàng)建 FastThreadLocalThread,基友搭配,干活不累,一定要配合使用才“快”。 三、FastThreadLocal 實(shí)戰(zhàn) 要使用 FastThreadLocal 就需要導(dǎo)入 Netty 的依賴了: 寫一個(gè)測(cè)試小示例: 結(jié)果輸出: 可以看出,在大量讀寫面前,寫操作的效率差不多,但讀操作 FastThreadLocal 比 ThreadLocal 快的不是一個(gè)數(shù)量級(jí),簡(jiǎn)直是秒殺 ThreadLocal 的存在。 當(dāng)我把 MAX 值調(diào)整到 1000 時(shí),結(jié)果輸出: 讀寫操作不多時(shí),ThreadLocal 明顯更勝一籌! 上面的示例是單線程測(cè)試多個(gè) *ThreadLocal,即數(shù)組形式,另外,我也測(cè)試了多線程單個(gè) *ThreadLocal,這時(shí)候 FastThreadLocal 效率就明顯要落后于 ThreadLocal。。 最后需要說明的是,在使用完 FastThreadLocal 之后不用 remove 了,因?yàn)樵?FastThreadLocalRunnable 中已經(jīng)加了移除邏輯,在線程運(yùn)行完時(shí)會(huì)移除全部綁定在當(dāng)前線程上的所有變量。 所以,使用 FastThreadLocal 導(dǎo)致內(nèi)存溢出的概率會(huì)不會(huì)要低于 ThreadLocal? 不一定,因?yàn)?FastThreadLocal 會(huì)產(chǎn)生大量的 index 常量,所謂的空間換時(shí)間,所以感覺 FastThreadLocal 內(nèi)存溢出的概率更大,但好在每次使用完都會(huì)自動(dòng) remove。 四、總結(jié) Netty 中的 FastThreadLocal 在大量頻繁讀寫操作時(shí)效率要高于 ThreadLocal,但要注意結(jié)合 Netty 自帶的線程類使用,這可能就是 Netty 為什么高性能的奧妙之一吧! 如果沒有大量頻繁讀寫操作的場(chǎng)景,JDK 自帶的 ThreadLocal 足矣,并且性能還要優(yōu)于 FastThreadLocal。 本文轉(zhuǎn)載自微信公眾號(hào)「Java技術(shù)棧」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java技術(shù)棧公眾號(hào)。