您好,登錄后才能下訂單哦!
這篇文章給大家介紹Netty的FastThreadLocal的原理及用法是什么,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
簡而言之,FastThreadLocal 是在 ThreadLocal 實現(xiàn)上的一種變種,相比 ThreadLocal 內(nèi)部通過將自身 hash 的方式在 hashTable 上定位需要的變量存儲位置,F(xiàn)astThreadLocal 選擇在數(shù)組上的一個固定的常量位置來存放線程本地變量,這樣的操作看起來并沒有太大區(qū)別,但是相比 ThreadLocal 的確體現(xiàn)了性能上的優(yōu)勢,尤其是在讀操作頻繁的場景下。
如果想要得到 FastThreadLocal 的速度優(yōu)勢,必須通過 FastThreadLocalThread 或者其子類的線程,才可以使用,因為這個原因,Netty 的 DefaultThreadFactory,其內(nèi)部默認(rèn)線程工廠的 newThread()方法就是直接初始化一個 FastThreadLocalThread ,以便期望在 ThreadLocal 的操作中,得到其性能上帶來的優(yōu)勢。
protected Thread newThread(Runnable r, String name) { return new FastThreadLocalThread(threadGroup, r, name);}
當(dāng)需要用到 FastThreadLocal 的時候,想必和 jdk 原生的 ThreadLocal 的 api 類似,都是通過初始化一個新的 FastThreadLocal 之后,通過其 set()方法初始化并放入一個變量作為線程本地變量存儲。
public final void set(V value) { if (value != InternalThreadLocalMap.UNSET) { set(InternalThreadLocalMap.get(), value); } else { remove(); }}
因此,在 FastThreadLocal 的 set()方法中,可以看到,存儲本地線程變量的數(shù)據(jù)結(jié)構(gòu)是一個 InternalThreadLocalMap。
private InternalThreadLocalMap threadLocalMap;
在 FastThreadLocalThread 中,因為本身 threadLocalMap 就是其中的一個成員,能夠快速得到返回。而其他線程實現(xiàn),就將面臨沒有這個成員的尷尬,Netty 也給出了相應(yīng)的兼容。
public static InternalThreadLocalMap get() { Thread thread = Thread.currentThread(); if (thread instanceof FastThreadLocalThread) { return fastGet((FastThreadLocalThread) thread); } else { return slowGet(); }}
InternalThreadLocalMap 的 get()方法中,當(dāng)前線程如果是 FastThreadLocalThread 或是其子類的實現(xiàn),變直接返回其 InternalThreadLocalMap 進(jìn)行操作,但對于不屬于上述條件的線程,Netty 通過 slowGet()的方式,也將返回一個 InternalThreadLocalMap。
private static InternalThreadLocalMap slowGet() { ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap; InternalThreadLocalMap ret = slowThreadLocalMap.get(); if (ret == null) { ret = new InternalThreadLocalMap(); slowThreadLocalMap.set(ret); } return ret;}
在 slowGet()方法中,當(dāng)前線程對應(yīng)的 InternalThreadLocalMap 會通過原生 jdk 下 ThreadLocal 的方式存儲并通過 ThreadLocal 返回,因此,在這個場景下,使用的還是 jdk 原生的 ThreadLocal,但是只占用了原生 ThreadLocal 下的 Entry[]數(shù)組的一個位置,具體的變量還是存放在專門為 FastThreadLocal 服務(wù)的 InternalThreadLocalMap 中。
在此,隨著 InternalThreadLocalMap 的得到并返回,針對 FastThreadLocal 的 get 和 set 操作,也將變?yōu)椴僮?InternalThreadLocalMap 來達(dá)到目的,F(xiàn)astThreadLocal 性能優(yōu)越的原因,也在 InternalThreadLocalMap 當(dāng)中。
static final AtomicInteger nextIndex = new AtomicInteger();Object[] indexedVariables;
InternalThreadlocalMap 主要由以上兩個成員組成,其中 indexedVariables 作為一個 Object[]數(shù)組,直接用來存放 FastThreadLocal 對應(yīng)的 value,每個 FastThreadLocal 對象都會在相應(yīng)的線程的 ThreadLocalMap 中被分配到對應(yīng)的 index,而這里的具體下標(biāo),則由以上的 nextIndex 成員在每個 FastThreadLocal 初始化的時候分配。
private final int index;public FastThreadLocal() { index = InternalThreadLocalMap.nextVariableIndex();}
每個 FastThreadLocal 在構(gòu)造方法的過程中,都會通過 InternalThreadlocalMap 的 nextVariableIndex()返回 nextIndex 自加后的結(jié)果作為其在 InternalThreadlocalMap 上的下標(biāo)。后續(xù)該 FastThreadLocal 在操作變量的時候可以直接通過該 index 定位到 Object[]數(shù)組上的位置。
private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
而數(shù)組上的下標(biāo)有一個特殊位,一般在其首位也就是 0 的位置,這個位置在 FastThreadLocal 類被加載的時候作為靜態(tài)變量被設(shè)置。在這個位置上,存放的是一個 FastThreadLocal 對象集合,每個存放到 InternalThreadlocalMap 中的 FastThreadLocal 都會被保存在首位的集合中。
public static final Object UNSET = new Object();
另外,為了具體區(qū)分保存的變量是 null 還是不存在當(dāng)前變量,InternalThreadLocalMap 中定義了一個為 NULL 的成員變量,以便區(qū)分上述情況,在一開始,InternalThreadLocalMap 中的 indexedVariables 數(shù)組都是 NULL。
相比 FastThreadLocal 的 set 操作,get 方法的過程與邏輯都要簡單的多,因此此處主要以其 set 方法為主。
public final void set(V value) { if (value != InternalThreadLocalMap.UNSET) { set(InternalThreadLocalMap.get(), value); } else { remove(); }}public final void set(InternalThreadLocalMap threadLocalMap, V value) { if (value != InternalThreadLocalMap.UNSET) { if (threadLocalMap.setIndexedVariable(index, value)) { addToVariablesToRemove(threadLocalMap, this); } } else { remove(threadLocalMap); }}
在其 set()方法中,首先會判斷 set 的值是否是 InternalThreadLocalMap 中的 NULL 對象來判斷是 set 操作還是 remove 操作,如果不是,會通過 InternalThreadLocalMap.get()方法獲取當(dāng)前線程對應(yīng)的 InternalThreadLocalMap,獲取的過程在前文已經(jīng)描述過。之后的主要流程主要分為兩步:
?調(diào)用 InternalThreadLocalMap 的 setIndexedVariable()方法,將該 FastThreadLocal 成員在構(gòu)造方法中獲得到的 InternalThreadLocalMap 上的下標(biāo)作為入?yún)魅搿?/p>public boolean setIndexedVariable(int index, Object value) { Object[] lookup = indexedVariables; if (index < lookup.length) { Object oldValue = lookup[index]; lookup[index] = value; return oldValue == UNSET; } else { expandIndexedVariableTableAndSet(index, value); return true; }}
在 InternalThreadLocalMap 的 setIndexedVariable()方法過程中,set 的過程并不復(fù)雜,找到對應(yīng)的下標(biāo),并將對應(yīng)的值放到 InternalThreadLocalMap 數(shù)組下標(biāo)對應(yīng)的位置上即宣告結(jié)束。但是,因為 FastThreadLocal 在構(gòu)造過程中雖然預(yù)先獲得了對應(yīng)的下標(biāo),但是實際上的數(shù)組大小可能完全還沒有達(dá)到相應(yīng)的大小,就要在此處通過 expandIndexedVariableTableAndSet()方法進(jìn)行擴(kuò)容,由于是數(shù)組的緣故,只需要擴(kuò)容后將原來的值復(fù)制過去,并將剩余的值用 NULL 對象填滿即可。
?如果上一步 set 成功,通過 addToVariablesToRemove()方法將該 FastThreadLocal 對象放入到 InternalThreadLocalMap 的數(shù)組中的首位集合中。在這個集合中,對于 FastThreadLocal 是一個強引用。
這樣,對于 FastThreadLocal 的一次 set 操作即宣告結(jié)束。
FastThreadLocal 在具體的定位的過程中,只需要根據(jù)在構(gòu)造方法里獲取得到的具體下標(biāo)就可以定位到具體的數(shù)組位置進(jìn)行變量的存取,而在 jdk 原生的 ThreadLocal 中,具體位置的下標(biāo)獲取不僅需要計算 ThreadLocal 的 hash 值,并需要在 hashTable 上根據(jù) key 定位的結(jié)果,一旦定位之后的結(jié)果上已經(jīng)存在其他 ThreadLocal 的變量,那么則是通過線性探測法,在 hashTable 上尋找下一個位置進(jìn)行,相比 FastThreadLocal 定位的過程要復(fù)雜的多。
FastThreadLocal 由于采取數(shù)組的方式,當(dāng)面對擴(kuò)容的時候,只需要將原數(shù)組中的內(nèi)容復(fù)制過去,并用 NULL 對象填滿剩余位置即可,而在 ThreadLocal 中,由于 hashTable 的緣故,在擴(kuò)容后還需要進(jìn)行一輪 rehash,在這過程中,仍舊存在 hash 沖突的可能。
在 FastThreadLocal 中,遍歷當(dāng)前線程的所有本地變量,只需要取數(shù)組首位的集合即可,不需要遍歷數(shù)組上的每一個位置。
在原生的 ThreadLocal 中,由于可能存在 ThreadLocal 被回收,但是當(dāng)前線程仍舊存活的情況導(dǎo)致 ThreadLocal 對應(yīng)的本地變量內(nèi)存泄漏的問題,因此在 ThreadLocal 的每次操作后,都會進(jìn)行啟發(fā)式的內(nèi)存泄漏檢測,防止這樣的問題產(chǎn)生,但也在每次操作后花費了額外的開銷。
而在 FastThreadLocal 的場景下,由于數(shù)組首位的 FastThreadLocal 集合中保持著所有 FastThreadLocal 對象的引用,因此當(dāng)外部的 FastThreadLocal 的引用被置為 null,該 FastThreadLocal 對象仍舊保持著這個集合的引用,不會被回收掉,只需要在線程當(dāng)前業(yè)務(wù)操作后,手動調(diào)用 FastThreadLocal 的 removeAll()方法,將會遍歷數(shù)組首位集合,回收掉所有 FastThreadLocal 的變量,避免內(nèi)存泄漏的產(chǎn)生,也減少了原生 ThreadLocal 的啟發(fā)式檢測開銷。
private static final class DefaultRunnableDecorator implements Runnable { private final Runnable r; DefaultRunnableDecorator(Runnable r) { this.r = r; } @Override public void run() { try { r.run(); } finally { FastThreadLocal.removeAll(); } }}
在 Netty 的 DefaultThreadFactory 中,每個線程在執(zhí)行為任務(wù)后都會調(diào)用 FastThreadLocal 的 removeAll()方法。
關(guān)于Netty的FastThreadLocal的原理及用法是什么就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,可以學(xué)到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。