Netty中使用FastThreadLocal替代JDK中的ThreadLocal 【JAVA】ThreadLocal源碼分析,其用法和ThreadLocal 一樣,只不過從名字FastThreadLocal來看,其處理效率要比JDK中的ThreadLocal要高
在類加載的時候,先初始化了一個靜態成員:
private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
實際上FastThreadLocal的操作都是通過對InternalThreadLocalMap的操作來實現的,
而InternalThreadLocalMap是UnpaddedInternalThreadLocalMap的子類,UnpaddedInternalThreadLocalMap的定義比較簡單:
class UnpaddedInternalThreadLocalMap {
static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal();
static final AtomicInteger nextIndex = new AtomicInteger();
Object[] indexedVariables;
int futureListenerStackDepth;
int localChannelReaderStackDepth;
Map<Class<?>, Boolean> handlerSharableCache;
IntegerHolder counterHashCode;
ThreadLocalRandom random;
Map<Class<?>, TypeParameterMatcher> typeParameterMatcherGetCache;
Map<Class<?>, Map<String, TypeParameterMatcher>> typeParameterMatcherFindCache;
StringBuilder stringBuilder;
Map<Charset, CharsetEncoder> charsetEncoderCache;
Map<Charset, CharsetDecoder> charsetDecoderCache;
ArrayList<Object> arrayList;
UnpaddedInternalThreadLocalMap(Object[] indexedVariables) {
this.indexedVariables = indexedVariables;
}
}
可以看到在類加載時,會初始化一個泛型爲InternalThreadLocalMap的JDK的ThreadLocal對象作爲其靜態成員slowThreadLocalMap ,還有一個原子化的Integer靜態成員nextIndex
InternalThreadLocalMap的定義如下:
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(InternalThreadLocalMap.class);
private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8;
private static final int STRING_BUILDER_INITIAL_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.initialSize", 1024);
private static final int STRING_BUILDER_MAX_SIZE;
public static final Object UNSET = new Object();
private BitSet cleanerFlags;
InternalThreadLocalMap的nextVariableIndex方法:
public static int nextVariableIndex() {
int index = nextIndex.getAndIncrement();
if (index < 0) {
nextIndex.decrementAndGet();
throw new IllegalStateException("too many thread-local indexed variables");
} else {
return index;
}
}
這是一個CAS滯後自增操作,獲取nextIndex自增前的值,那麼variablesToRemoveIndex初始化時就是0,且恆爲0,nextIndex此時變成了1
FastThreadLocal對象的初始化:
private final int index = InternalThreadLocalMap.nextVariableIndex();
public FastThreadLocal() {
}
由上面可知,index成員恆等於nextVariableIndex的返回值,nextIndex 的CAS操作保障了每個FastThreadLocal對象的index是不同的
首先看到set方法:
public final void set(V value) {
if (value != InternalThreadLocalMap.UNSET) {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
if (this.setKnownNotUnset(threadLocalMap, value)) {
this.registerCleaner(threadLocalMap);
}
} else {
this.remove();
}
}
只要set的value不是InternalThreadLocalMap.UNSET,會先調用InternalThreadLocalMap的get方法:
public static InternalThreadLocalMap get() {
Thread thread = Thread.currentThread();
return thread instanceof FastThreadLocalThread ? fastGet((FastThreadLocalThread)thread) : slowGet();
}
判斷當前線程是否是FastThreadLocalThread,是則調用fastGet,否則調用slowGet
FastThreadLocalThread是經過包裝後的Thread:
public class FastThreadLocalThread extends Thread {
private final boolean cleanupFastThreadLocals;
private InternalThreadLocalMap threadLocalMap;
public FastThreadLocalThread() {
this.cleanupFastThreadLocals = false;
}
public FastThreadLocalThread(Runnable target) {
super(FastThreadLocalRunnable.wrap(target));
this.cleanupFastThreadLocals = true;
}
public FastThreadLocalThread(ThreadGroup group, Runnable target) {
super(group, FastThreadLocalRunnable.wrap(target));
this.cleanupFastThreadLocals = true;
}
public FastThreadLocalThread(String name) {
super(name);
this.cleanupFastThreadLocals = false;
}
public FastThreadLocalThread(ThreadGroup group, String name) {
super(group, name);
this.cleanupFastThreadLocals = false;
}
public FastThreadLocalThread(Runnable target, String name) {
super(FastThreadLocalRunnable.wrap(target), name);
this.cleanupFastThreadLocals = true;
}
public FastThreadLocalThread(ThreadGroup group, Runnable target, String name) {
super(group, FastThreadLocalRunnable.wrap(target), name);
this.cleanupFastThreadLocals = true;
}
public FastThreadLocalThread(ThreadGroup group, Runnable target, String name, long stackSize) {
super(group, FastThreadLocalRunnable.wrap(target), name, stackSize);
this.cleanupFastThreadLocals = true;
}
public final InternalThreadLocalMap threadLocalMap() {
return this.threadLocalMap;
}
public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) {
this.threadLocalMap = threadLocalMap;
}
public boolean willCleanupFastThreadLocals() {
return this.cleanupFastThreadLocals;
}
public static boolean willCleanupFastThreadLocals(Thread thread) {
return thread instanceof FastThreadLocalThread && ((FastThreadLocalThread)thread).willCleanupFastThreadLocals();
}
}
如果看過我之前寫的ThreadLocal源碼分析,看到這就明白,JDK的ThreadLocal中很重要的一點是在Thread類中有一個ThreadLocalMap類型的成員,每個線程都維護這一張ThreadLocalMap,通過ThreadLocalMap來和ThreadLocal對象產生映射關係;而這裏和JDK同理綁定的就是InternalThreadLocalMap。
fastGet方法:
private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
if (threadLocalMap == null) {
thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
}
return threadLocalMap;
}
這裏也和JDK的ThreadLocal類似,判斷FastThreadLocalThread 線程的threadLocalMap成員是否爲null,若是null,則先創建一個InternalThreadLocalMap實例:
private InternalThreadLocalMap() {
super(newIndexedVariableTable());
}
先調用newIndexedVariableTable方法:
private static Object[] newIndexedVariableTable() {
Object[] array = new Object[32];
Arrays.fill(array, UNSET);
return array;
}
創建了一個大小爲32的數組,並且用UNSET這個Object填充了整個數組,然後調用UnpaddedInternalThreadLocalMap的構造,令indexedVariables成員保存該數組
再來看slowGet方法:
private static InternalThreadLocalMap slowGet() {
ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
InternalThreadLocalMap ret = (InternalThreadLocalMap)slowThreadLocalMap.get();
if (ret == null) {
ret = new InternalThreadLocalMap();
slowThreadLocalMap.set(ret);
}
return ret;
}
可以看到,其實這裏爲了提高效率,並沒有直接使用JDK的ThreadLocal,而是給當前非FastThreadLocalThread線程綁定了一個ThreadLocal對象,避免直接使用JDK的ThreadLocal效率低。
回到FastThreadLocal的set方法,在取得到了當前線程的InternalThreadLocalMap成員後,調用setKnownNotUnset方法:
private boolean setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
if (threadLocalMap.setIndexedVariable(this.index, value)) {
addToVariablesToRemove(threadLocalMap, this);
return true;
} else {
return false;
}
}
首先調用了InternalThreadLocalMap的setIndexedVariable方法:
public boolean setIndexedVariable(int index, Object value) {
Object[] lookup = this.indexedVariables;
if (index < lookup.length) {
Object oldValue = lookup[index];
lookup[index] = value;
return oldValue == UNSET;
} else {
this.expandIndexedVariableTableAndSet(index, value);
return true;
}
}
因爲index是不可更改的常量,所以這裏有兩種情況:
當indexedVariables這個Object數組的長度大於index時,直接將value放在indexedVariables數組下標爲index的位置,返回oldValue是否等於UNSET,若是不等於UNSET,說明已經set過了,直進行替換,若是等於UNSET,還要進行後續的registerCleaner
當indexedVariables這個Object數組的長度小於等於index時,調用expandIndexedVariableTableAndSet方法擴容
expandIndexedVariableTableAndSet方法:
private void expandIndexedVariableTableAndSet(int index, Object value) {
Object[] oldArray = this.indexedVariables;
int oldCapacity = oldArray.length;
int newCapacity = index | index >>> 1;
newCapacity |= newCapacity >>> 2;
newCapacity |= newCapacity >>> 4;
newCapacity |= newCapacity >>> 8;
newCapacity |= newCapacity >>> 16;
++newCapacity;
Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
newArray[index] = value;
this.indexedVariables = newArray;
}
如果讀過HashMap源碼的話對上述的位運算操作因該不陌生,這個位運算產生的newCapacity的值是大於oldCapacity的最小的二的整數冪
【Java】HashMap中的tableSizeFor方法
然後申請一個newCapacity大小的數組,將原數組的內容拷貝到新數組,並且用UNSET填充剩餘部分,還是將value放在下標爲index的位置,用indexedVariables保存新數組。
setIndexedVariable成立後,setKnownNotUnset繼續調用addToVariablesToRemove方法:
private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
Set variablesToRemove;
if (v != InternalThreadLocalMap.UNSET && v != null) {
variablesToRemove = (Set)v;
} else {
variablesToRemove = Collections.newSetFromMap(new IdentityHashMap());
threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);
}
variablesToRemove.add(variable);
}
上面說過variablesToRemoveIndex恆爲0,調用InternalThreadLocalMap的indexedVariable方法:
public Object indexedVariable(int index) {
Object[] lookup = this.indexedVariables;
return index < lookup.length ? lookup[index] : UNSET;
}
由於variablesToRemoveIndex恆等於0,所以這裏判斷indexedVariables這個Object數組是否爲空,若是爲空,則返回第0個元素,若不是則返回UNSET
在addToVariablesToRemove中,接着對indexedVariables的返回值進行了判斷,
判斷不是UNSET,並且不等於null,則說明是set過的,然後將剛纔的返回值強轉爲Set類型
若上述條件不成立,創建一個IdentityHashMap,將其包裝成Set賦值給variablesToRemove,然後調用InternalThreadLocalMap的setIndexedVariable方法,這裏就和上面不一樣了,上面是將value放在下標爲index的位置,而這裏是將Set放在下標爲0的位置。
看到這,再結合上面來看,其實已經有一個大致的想法了,一開始在set時,是將value放在InternalThreadLocalMap的Object數組下標爲index的位置,然後在這裏獲取下標爲0的Set,說明value是暫時放在下標爲index的位置,然後判斷下標爲0的位置有沒有Set,若是有,取出這個Set ,將當前FastThreadLocal對象放入Set中,則說明這個Set中存放的是FastThreadLocal集合
那麼就有如下關係:
回到FastThreadLocal的set方法,在setKnownNotUnset成立後,調用registerCleaner方法:
private void registerCleaner(InternalThreadLocalMap threadLocalMap) {
Thread current = Thread.currentThread();
if (!FastThreadLocalThread.willCleanupFastThreadLocals(current) && !threadLocalMap.isCleanerFlagSet(this.index)) {
threadLocalMap.setCleanerFlag(this.index);
}
}
willCleanupFastThreadLocals的返回值在前面FastThreadLocalThread的初始化時就確定了,看到isCleanerFlagSet方法:
public boolean isCleanerFlagSet(int index) {
return this.cleanerFlags != null && this.cleanerFlags.get(index);
}
cleanerFlags 是一個BitSet對象,在InternalThreadLocalMap初始化時是null,
若不是第一次的set操作,則根據index,獲取index在BitSet對應位的值
這裏使用BitSet,使其持有的位和indexedVariables這個Object數組形成了一一對應關係,每一位都是0和1代表當前indexedVariables的對應下標位置的使用情況,0表示沒有使用對應UNSET,1則代表有value
在上面條件成立的情況下,調用setCleanerFlag方法:
public void setCleanerFlag(int index) {
if (this.cleanerFlags == null) {
this.cleanerFlags = new BitSet();
}
this.cleanerFlags.set(index);
}
邏輯比較簡單,判斷cleanerFlags是否初始化,若沒有,則立即初始化,再將cleanerFlags中對應index位的值設爲1;
這裏通過registerCleaner直接標記了所有set了value的下標可,爲以後的removeAll 清除提高效率。
下來看FastThreadLocal的get方法:
public final V get() {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
Object v = threadLocalMap.indexedVariable(this.index);
if (v != InternalThreadLocalMap.UNSET) {
return v;
} else {
V value = this.initialize(threadLocalMap);
this.registerCleaner(threadLocalMap);
return value;
}
}
和上面一樣,先取得當前線程持有的InternalThreadLocalMap ,調用indexedVariable方法,根據當前FastThreadLocal的index定位,判斷是否是UNSET(set過),若沒有set過則和JDK一樣調用initialize先set:
private V initialize(InternalThreadLocalMap threadLocalMap) {
Object v = null;
try {
v = this.initialValue();
} catch (Exception var4) {
PlatformDependent.throwException(var4);
}
threadLocalMap.setIndexedVariable(this.index, v);
addToVariablesToRemove(threadLocalMap, this);
return v;
}
initialValue()方法就是對外提供的,需要手動覆蓋:
protected V initialValue() throws Exception {
return null;
}
後面的操作就和set的邏輯一樣。
remove方法:
public final void remove() {
this.remove(InternalThreadLocalMap.getIfSet());
}
getIfSet方法:
public static InternalThreadLocalMap getIfSet() {
Thread thread = Thread.currentThread();
return thread instanceof FastThreadLocalThread ? ((FastThreadLocalThread)thread).threadLocalMap() : (InternalThreadLocalMap)slowThreadLocalMap.get();
}
和上面的get方法思路相似,只不過在這裏如果獲取不到不會創建
然後調用remove重載:
public final void remove(InternalThreadLocalMap threadLocalMap) {
if (threadLocalMap != null) {
Object v = threadLocalMap.removeIndexedVariable(this.index);
removeFromVariablesToRemove(threadLocalMap, this);
if (v != InternalThreadLocalMap.UNSET) {
try {
this.onRemoval(v);
} catch (Exception var4) {
PlatformDependent.throwException(var4);
}
}
}
}
先檢查threadLocalMap是否存在,若存在才進行後續操作:
調用removeIndexedVariable方法:
public Object removeIndexedVariable(int index) {
Object[] lookup = this.indexedVariables;
if (index < lookup.length) {
Object v = lookup[index];
lookup[index] = UNSET;
return v;
} else {
return UNSET;
}
}
和之前的setIndexedVariable邏輯相似,只不過現在是把index位置的元素設置爲UNSET
接着調用removeFromVariablesToRemove方法:
private static void removeFromVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
if (v != InternalThreadLocalMap.UNSET && v != null) {
Set<FastThreadLocal<?>> variablesToRemove = (Set)v;
variablesToRemove.remove(variable);
}
}
之前說過variablesToRemoveIndex恆爲0,在Object數組中下標爲0存儲的Set<FastThreadLocal<?>>集合(不爲UNSET情況下),從集合中,將當前FastThreadLocal移除掉
最後調用了onRemoval方法,該方法需要由用戶去覆蓋:
protected void onRemoval(V value) throws Exception {
}
removeAll方法,是一個靜態方法:
public static void removeAll() {
InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
if (threadLocalMap != null) {
try {
Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
if (v != null && v != InternalThreadLocalMap.UNSET) {
Set<FastThreadLocal<?>> variablesToRemove = (Set)v;
FastThreadLocal<?>[] variablesToRemoveArray = (FastThreadLocal[])variablesToRemove.toArray(new FastThreadLocal[0]);
FastThreadLocal[] var4 = variablesToRemoveArray;
int var5 = variablesToRemoveArray.length;
for(int var6 = 0; var6 < var5; ++var6) {
FastThreadLocal<?> tlv = var4[var6];
tlv.remove(threadLocalMap);
}
}
} finally {
InternalThreadLocalMap.remove();
}
}
}
首先獲取當前線程的InternalThreadLocalMap,若是存在繼續後續操作:
通過indexedVariable方法,取出Object數組中下標爲0的Set集合(如果不是UNSET情況下),將其轉換爲FastThreadLocal數組,遍歷這個數組調用上面的remove方法。
FastThreadLocal源碼分析到此結束。