FastThreadLocal
的類名自己就充滿了對ThreadLocal
的挑釁,「快男」FastThreadLocal是怎麼快的?源碼中類註釋坦白以下:html
/** * ... * 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. * ... */
大概意思就是:用索引
代替了ThreadLocal中的threadLocalHashCode
,當請求頻繁時,這個小改動就會顯現其效果。
提到FastThreadLocal,就不得不提它的好基友FastThreadLocalThread
,簡單來講,FastThreadLocal就是爲FastThreadLocalThread量身打造的!java
FastThreadLocalThread又是哪一個單位的?且聽我慢慢道來……數組
通常來講,Netty的client端,是這麼建立的:併發
EventLoopGroup group = new NioEventLoopGroup();
沿着調用鏈,層層深刻NioEventLoopGroup的構造函數,在MultithreadEventExecutorGroup
的構造塊,會看到這樣的邏輯:函數
if (executor == null) { executor = new ThreadPerTaskExecutor(newDefaultThreadFactory()); }
newDefaultThreadFactory()
方法,功能如其名,建立了DefaultThreadFactory
工廠:oop
protected ThreadFactory newDefaultThreadFactory() { return new DefaultThreadFactory(getClass()); }
而DefaultThreadFactory工廠的產品,就是FastThreadLocalThread。this
ok,FastThreadLocalThread物種起源的事兒暫且放下,咱們來簡單過一下原版ThreadLocal的工做機制。spa
ThreadLocal<T>
你們應該並不陌生,兩個最核心API線程
//賦值方法 public void set(T value); //取值方法 public T get();
用以併發環境下,線程生命週期內的存取數據操做,隔離其它線程干擾。code
幫助理解的示意圖:
Thread中有一屬性threadLocals
,ThreadLocal.ThreadLocalMap
類型(ThreadLocalMap是ThreadLocal的靜態內部類)。ThreadLocal的set(T value)
方法被調用時,會將參數value
存放於當前線程的threadLocals
中,想要獲取時,再從threadLocals
中獲取。
剛剛說過,threadLocals是一個ThreadLocalMap(ThreadLocal中的靜態內部類),Entry則是ThreadLocalMap的內部節點
。而ThreadLocalMap做爲一個Map,人設是這樣的ThreadLocalMap<ThreadLocal<T>>,T>
,即key=ThreadLocal<T>,value=T。
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //根據當前線程獲取ThreadLocalMap if (map != null) map.set(this, value); //map的key直接使用的當前ThreadLocal對象 else createMap(t, value); }
線程向ThreadLocal存入時,第一次調用將在線程中分配一塊空間,初始大小爲Entry[16]數組
。而後,以做爲key的ThreadLocal<T>計算出hashCode
,稍加計算得出Entry[16]數組
的索引i
。最後,爲槽位Entry[i]賦值上value。
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //找到與當前線程綁定的ThreadLocalMap if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
獲取時,先找到與當前線程綁定的ThreadLocalMap,而後你懂得……
聽上去一副滿滿的HashMap的套路,其實內在有很大差異,好比ThreadLocal的Entry實現了WeakReference
(弱引用)。
ThreadLocal爲何用Entry數組做爲內置實現?並且初始就16位,還有擴容等實現。對某一線程而言,ThreadLocal不是隻能存儲一個值嗎?這麼想的話,用單個Object就能存儲了……
這個萬惡的問題,當時困擾了我很長時間,其實想通後很簡單:
Entry數組爲了實現ThreadLocalMap,而ThreadLocalMap君的key是ThreadLocal,多個ThreadLocal均可以存儲在ThreadLocalMap中。沒錯,A線程只用一個ThreadLocal的話,確實用一個Object做爲內置實現就搞定了,可是A線程使用多個ThreadLocal時,沒法知足!
ThreadLocal就分析到這裏,讓咱們迴歸FastThreadLocal。
結構及關鍵方法示意圖:
前文提到過,FastThreadLocal的關鍵是index
,index
能夠理解成一個FastThreadLocal對象的惟一ID。這個index會在FastThreadLocalThread線程中,做爲其indexedVariables
屬性(初始是一個Object[32]數組)的索引,達成與ThreadLocal相似的效果。
值得一提的是Object[] indexedVariables
的第0位(Object[0]),用於存放將來會被釋放的數據,其實存儲的就是以前FastThreadLocal的set方法操做過的槽位信息。FastThreadLocal的remove方法被調用時,會根據記錄的槽位信息進行大掃除。
FastThreadLocal相關內容就聊到這裏,更多細節請翻看源碼!