Netty源碼分析第8章(高性能工具類FastThreadLocal和Recycler)---->第1節: FastThreadLocal的使用和建立

 

Netty源碼分析第八章: 高性能工具類FastThreadLocal和Recyclerhtml

 

概述:數組

 

        FastThreadLocal咱們在剖析堆外內存分配的時候簡單介紹過, 它相似於JDK的ThreadLocal, 也是用於在多線程條件下, 保證統一線程的對象共享, 只是netty中定義的FastThreadLocal, 性能要高於jdk的ThreadLocal, 具體緣由會在以後的小節進行剖析多線程

 

        Recyler咱們應該也不會太陌生, 由於在以前章節中, 有好多地方使用了Recyler併發

 

        Recyler是netty實現的一個輕量級對象回收站, 不少對象在使用完畢以後, 並無直接交給gc去處理, 而是經過對象回收站將對象回收, 目的是爲了對象重用和減小gc壓力ide

 

        好比ByteBuf對象的回收, 由於ByteBuf對象在netty中會頻繁建立, 而且會佔用比較大的內存空間, 因此使用完畢後會經過對象回收站的方式進行回收, 已達到資源重用的目的工具

 

        這一章就對FastThreadLocal和Recyler兩個併發工具類進行分析oop

 

第一節:FastThreadLocal的使用和建立源碼分析

 

 

首先咱們看一個最簡單的demo:性能

public class FastThreadLocalDemo { final class FastThreadLocalTest extends FastThreadLocal<Object>{ @Override protected Object initialValue() throws Exception { return new Object(); } } private final FastThreadLocalTest fastThreadLocalTest; public FastThreadLocalDemo(){ fastThreadLocalTest = new FastThreadLocalTest(); } public static void main(String[] args){ FastThreadLocalDemo fastThreadLocalDemo = new FastThreadLocalDemo(); new Thread(new Runnable() { @Override public void run() { Object obj = fastThreadLocalDemo.fastThreadLocalTest.get(); try { for (int i=0;i<10;i++){ fastThreadLocalDemo.fastThreadLocalTest.set(new Object()); Thread.sleep(1000); } }catch (Exception e){ e.printStackTrace(); } } }).start(); new Thread(new Runnable() { @Override public void run() { try { Object obj = fastThreadLocalDemo.fastThreadLocalTest.get(); for (int i=0;i<10;i++){ System.out.println(obj == fastThreadLocalDemo.fastThreadLocalTest.get()); Thread.sleep(1000); } }catch (Exception e){ } } }).start(); } }

這裏首先聲明一個內部類FastThreadLocalTest繼承FastThreadLocal, 並重寫initialValue方法, initialValue方法就是用來初始化線程共享對象的this

而後聲明一個成員變量fastThreadLocalTest, 類型就是內部類FastThreadLocalTest

在構造方法中初始化fastThreadLocalTest

 

main方法中建立當前類FastThreadLocalDemo的對象fastThreadLocalDemo

而後啓動兩個線程, 每一個線程經過fastThreadLocalDemo.fastThreadLocalTest.get()的方式拿到線程共享對象, 由於fastThreadLocalDemo是相同的, 因此fastThreadLocalTest對象也是同一個, 同一個對象在不一樣線程中進行get()

第一個線程循環經過set方法修改共享對象的值

第二個線程則循環判斷並輸出fastThreadLocalTest.get()出來的對象和第一次get出來的對象是否相等

這裏輸出結果都true, 說明其餘線程雖然不斷修改共享對象的值, 但都不影響當前線程共享對象的值

這樣就實現了線程共享的對象的功能

 

根據上述示例, 咱們剖析FastThreadLocal的建立

首先跟到FastThreadLocal的構造方法中:

public FastThreadLocal() { index = InternalThreadLocalMap.nextVariableIndex(); }

這裏的index, 表明FastThreadLocal對象的一個下標, 每建立一個FastThreadLocal, 都會有一個惟一的自增的下標

跟到nextVariableIndex方法中:

public static int nextVariableIndex() { int index = nextIndex.getAndIncrement(); if (index < 0) { nextIndex.decrementAndGet(); throw new IllegalStateException("too many thread-local indexed variables"); } return index; }

這裏只是獲取nextIndex經過getAndIncrement()進行原子自增, 建立第一個FastThreadLocal對象時, nextIndex爲0, 建立第二個FastThreadLocal對象時nextIndex爲1, 以此類推, 第n次nextIndex爲n-1, 如圖所示

8-1-1

咱們回到demo中, 咱們看線程中的這一句:

Object obj = fastThreadLocalDemo.fastThreadLocalTest.get();

這裏調用了FastThreadLocal對象的get方法, 做用是建立一個線程共享對象

咱們跟到get方法中:

public final V get() { return get(InternalThreadLocalMap.get()); }

這裏調用了一個重載的get方法, 參數中經過InternalThreadLocalMap的get方法獲取了一個InternalThreadLocalMap對象

咱們跟到InternalThreadLocalMap的get方法中, 分析其實如何獲取InternalThreadLocalMap對象的

public static InternalThreadLocalMap get() { Thread thread = Thread.currentThread(); if (thread instanceof FastThreadLocalThread) { return fastGet((FastThreadLocalThread) thread); } else { return slowGet(); } }

這裏首先拿到當前線程, 而後判斷當前線程是否爲FastThreadLocalThread線程, 一般NioEventLoop線程都是FastThreadLocalThread, 用於線程則不是FastThreadLocalThread

在這裏, 若是FastThreadLocalThread線程, 則調用fastGet方法獲取InternalThreadLocalMap, 從名字上咱們能知道, 這是一種效率極高的獲取方式

若是不是FastThreadLocalThread線程, 則調用slowGet方式獲取InternalThreadLocalMap, 一樣根據名字, 咱們知道這是一種效率不過高的獲取方式

咱們的demo並非eventLoop線程, 因此這裏會走到slowGet()方法中

咱們首先剖析slowGet()方法:

private static InternalThreadLocalMap slowGet() { ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap; InternalThreadLocalMap ret = slowThreadLocalMap.get(); if (ret == null) { ret = new InternalThreadLocalMap(); slowThreadLocalMap.set(ret); } return ret; }

首先經過UnpaddedInternalThreadLocalMap.slowThreadLocalMap拿到一個ThreadLocal對象slowThreadLocalMap, slowThreadLocalMap是UnpaddedInternalThreadLocalMap類的一個靜態屬性, 類型是ThreadLocal類型

這裏的ThreadLocal是jdk的ThreadLocal

而後經過slowThreadLocalMap對象的get方法, 獲取一個InternalThreadLocalMap

若是第一次獲取, InternalThreadLocalMap有多是null, 因此在if塊中, new了一個InternalThreadLocalMap對象, 並設置在ThreadLocal對象中

由於netty實現的FastThreadLocal要比jdk的ThreadLocal要快, 因此這裏的方法叫slowGet

回到InternalThreadLocalMap的get方法:

public static InternalThreadLocalMap get() { Thread thread = Thread.currentThread(); if (thread instanceof FastThreadLocalThread) { return fastGet((FastThreadLocalThread) thread); } else { return slowGet(); } }

咱們繼續剖析fastGet方法, 一般EventLoop線程FastThreadLocalThread線程, 因此EventLoop線程執行到這一步的時候會調用fastGet方法

咱們跟進fastGet:

private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) { InternalThreadLocalMap threadLocalMap = thread.threadLocalMap(); if (threadLocalMap == null) { thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap()); } return threadLocalMap; }

首先FastThreadLocalThread對象直接經過threadLocalMap拿到threadLocalMap對象

若是threadLocalMap爲null, 則建立一個InternalThreadLocalMap對象設置到FastThreadLocalThread的成員變量中

這裏咱們能夠知道FastThreadLocalThread對象中維護了一個InternalThreadLocalMap類型的成員變量, 能夠直接經過threadLocalMap()方法獲取該變量的值, 也就是InternalThreadLocalMap

咱們跟到InternalThreadLocalMap的構造方法中:

private InternalThreadLocalMap() { super(newIndexedVariableTable()); }

這裏調用了父類的構造方法, 傳入一個newIndexedVariableTable()

咱們跟到newIndexedVariableTable()中:

private static Object[] newIndexedVariableTable() { Object[] array = new Object[32]; Arrays.fill(array, UNSET); return array; }

這裏建立一個長度爲32的數組, 併爲數組中的每個對象設置爲UNSET, UNSET是一個Object的對象, 表示該下標的值沒有被設置

回到InternalThreadLocalMap的構造方法, 再看其父類的構造方法:

UnpaddedInternalThreadLocalMap(Object[] indexedVariables) { this.indexedVariables = indexedVariables; }

這裏初始化了一個數組類型的成員變量indexedVariables, 就是newIndexedVariableTable返回object的數組

這裏咱們能夠知道, 每一個InternalThreadLocalMap對象中都維護了一個Object類型的數組, 那麼這個數組有什麼做用呢?咱們繼續往下剖析

回到FastThreadLocal的get方法中:

public final V get() { return get(InternalThreadLocalMap.get()); }

咱們剖析完了InternalThreadLocalMap.get()的相關邏輯, 再繼續看重載的get方法:

public final V get(InternalThreadLocalMap threadLocalMap) { Object v = threadLocalMap.indexedVariable(index); if (v != InternalThreadLocalMap.UNSET) { return (V) v; } return initialize(threadLocalMap); }

首先看這一步:

Object v = threadLocalMap.indexedVariable(index);

這一步是拿到當前index下標的object, 其實也就是拿到每一個FastThreadLocal對象的綁定的線程共享對象

index是咱們剛纔分析過, 是每個FastThreadLocal的惟一下標

咱們跟到indexedVariable方法中:

public Object indexedVariable(int index) { Object[] lookup = indexedVariables; return index < lookup.length? lookup[index] : UNSET; }

這裏首先拿到indexedVariables, 咱們剛纔分析過, indexedVariables是InternalThreadLocalMap對象中維護的數組, 初始大小是32

而後再return中判斷當前index是否是小於當前數組的長度, 若是小於則獲取當前下標index的數組元素, 不然返回UNSET表明沒有設置的對象

這裏咱們能夠分析到, 其實每個FastThreadLocal對象中所綁定的線程共享對象, 是存放在threadLocalMap對象中的一個對象數組的中的, 數組中的元素的下標其實就是對應着FastThreadLocal中的index屬性, 對應關係如圖所示

8-1-2

回到FastThreadLocal重載的get方法:

public final V get(InternalThreadLocalMap threadLocalMap) { Object v = threadLocalMap.indexedVariable(index); if (v != InternalThreadLocalMap.UNSET) { return (V) v; } return initialize(threadLocalMap); }

根據以上邏輯, 咱們知道, 第一次獲取對象v是隻能獲取到UNSET對象, 由於該對象並無保存在threadLocalMap中的數組indexedVariables中, 因此第一次獲取在if判斷中爲false, 會走到initialize方法中

跟到initialize方法中:

private V initialize(InternalThreadLocalMap threadLocalMap) { V v = null; try { v = initialValue(); } catch (Exception e) { PlatformDependent.throwException(e); } threadLocalMap.setIndexedVariable(index, v); addToVariablesToRemove(threadLocalMap, this); return v; }

這裏首先調用的initialValue方法, 這裏的initialValue實際上走的是FastThreadLocal子類的重寫initialValue方法

在咱們的demo中對應這個方法:

@Override protected Object initialValue() throws Exception { return new Object(); }

經過這個方法會建立一個線程共享對象

而後經過threadLocalMap對象的setIndexedVariable方法將建立的線程共享對象設置到threadLocalMap中維護的數組中, 參數爲FastThreadLocal和建立的對象自己

跟到setIndexedVariable方法中:

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; } }

這裏首先判斷FastThreadLocal對象的index是否超過數組indexedVariables的長度, 若是沒有超過, 則直接經過下標設置新建立的線程共享對象, 經過這個操做, 下次獲取該對象的時候就能夠直接經過數組下標進行取出

若是index超過了數組indexedVariables的長度, 則經過expandIndexedVariableTableAndSet方法將數組擴容, 而且根據index的經過數組下標的方式將線程共享對象設置到數組indexedVariables中

以上就是線程共享對象的建立和獲取的過程

 

上一節: Future和Promies

下一節: FastThreadLocal的set方法

相關文章
相關標籤/搜索