Netty in FastThreadLocal source code analysis

Netty used FastThreadLocal replace the JDK ThreadLocal [JAVA] ThreadLocal source code analysis , and its use ThreadLocal same, but from the name FastThreadLocal view, the processing efficiency of the JDK is higher than ThreadLocal

When the class is loaded, initialize a static member:

1 private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();

In fact FastThreadLocal operations are through the operation of InternalThreadLocalMap to achieve,

The InternalThreadLocalMap is a subclass of UnpaddedInternalThreadLocalMap, UnpaddedInternalThreadLocalMap definition is relatively simple:

 1 class UnpaddedInternalThreadLocalMap {
 2     static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal();
 3     static final AtomicInteger nextIndex = new AtomicInteger();
 4     Object[] indexedVariables;
 5     int futureListenerStackDepth;
 6     int localChannelReaderStackDepth;
 7     Map<Class<?>, Boolean> handlerSharableCache;
 8     IntegerHolder counterHashCode;
 9     ThreadLocalRandom random;
10     Map<Class<?>, TypeParameterMatcher> typeParameterMatcherGetCache;
11     Map<Class<?>, Map<String, TypeParameterMatcher>> typeParameterMatcherFindCache;
12     StringBuilder stringBuilder;
13     Map<Charset, CharsetEncoder> charsetEncoderCache;
14     Map<Charset, CharsetDecoder> charsetDecoderCache;
15     ArrayList<Object> arrayList;
16 
17     UnpaddedInternalThreadLocalMap(Object[] indexedVariables) {
18         this.indexedVariables = indexedVariables;
19     }
20 }

Can be seen when the class is loaded, initialized as a generic ThreadLocal objects InternalThreadLocalMap of the JDK as a static member slowThreadLocalMap, there is one atom of Integer static member nextIndex

InternalThreadLocalMap defined as follows:

1 public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
2     private static final InternalLogger logger = InternalLoggerFactory.getInstance(InternalThreadLocalMap.class);
3     private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8;
4     private static final int STRING_BUILDER_INITIAL_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.initialSize", 1024);
5     private static final int STRING_BUILDER_MAX_SIZE;
6     public static final Object UNSET = new Object();
7     private BitSet cleanerFlags;

InternalThreadLocalMap of nextVariableIndex method:

1 public static int nextVariableIndex() {
2     int index = nextIndex.getAndIncrement();
3     if (index < 0) {
4         nextIndex.decrementAndGet();
5         throw new IllegalStateException("too many thread-local indexed variables");
6     } else {
7         return index;
8     }
9 }

This is a self-energizing lag CAS operation, to obtain the value before the increment nextIndex, then initialization is 0:00 variablesToRemoveIndex and constant at 0, this case becomes 1 nextIndex

FastThreadLocal object initialization:

1 private final int index = InternalThreadLocalMap.nextVariableIndex();
2 
3 public FastThreadLocal() {
4 }

As is clear from the above, index members nextVariableIndex return value is equal to a constant, nextIndex protect the operation of the CAS index for each object are different FastThreadLocal

First see the set method:

 1 public final void set(V value) {
 2     if (value != InternalThreadLocalMap.UNSET) {
 3         InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
 4         if (this.setKnownNotUnset(threadLocalMap, value)) {
 5             this.registerCleaner(threadLocalMap);
 6         }
 7     } else {
 8         this.remove();
 9     }
10 
11 }

As long as the value is not set InternalThreadLocalMap.UNSET, we will first call the get method InternalThreadLocalMap:

1 public static InternalThreadLocalMap get() {
2     Thread thread = Thread.currentThread();
3     return thread instanceof FastThreadLocalThread ? fastGet((FastThreadLocalThread)thread) : slowGet();
4 }

Determine whether the current thread is FastThreadLocalThread, it is the call fastGet, or call slowGet
FastThreadLocalThread is after packing Thread:

 1 public class FastThreadLocalThread extends Thread {
 2     private final boolean cleanupFastThreadLocals;
 3     private InternalThreadLocalMap threadLocalMap;
 4 
 5     public FastThreadLocalThread() {
 6         this.cleanupFastThreadLocals = false;
 7     }
 8 
 9     public FastThreadLocalThread(Runnable target) {
10         super(FastThreadLocalRunnable.wrap(target));
11         this.cleanupFastThreadLocals = true;
12     }
13 
14     public FastThreadLocalThread(ThreadGroup group, Runnable target) {
15         super(group, FastThreadLocalRunnable.wrap(target));
16         this.cleanupFastThreadLocals = true;
17     }
18 
19     public FastThreadLocalThread(String name) {
20         super(name);
21         this.cleanupFastThreadLocals = false;
22     }
23 
24     public FastThreadLocalThread(ThreadGroup group, String name) {
25         super(group, name);
26         this.cleanupFastThreadLocals = false;
27     }
28 
29     public FastThreadLocalThread(Runnable target, String name) {
30         super(FastThreadLocalRunnable.wrap(target), name);
31         this.cleanupFastThreadLocals = true;
32     }
33 
34     public FastThreadLocalThread(ThreadGroup group, Runnable target, String name) {
35         super(group, FastThreadLocalRunnable.wrap(target), name);
36         this.cleanupFastThreadLocals = true;
37     }
38 
39     public FastThreadLocalThread(ThreadGroup group, Runnable target, String name, long stackSize) {
40         super(group, FastThreadLocalRunnable.wrap(target), name, stackSize);
41         this.cleanupFastThreadLocals = true;
42     }
43 
44     public final InternalThreadLocalMap threadLocalMap() {
45         return this.threadLocalMap;
46     }
47 
48     public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) {
49         this.threadLocalMap = threadLocalMap;
50     }
51 
52     public boolean willCleanupFastThreadLocals() {
53         return this.cleanupFastThreadLocals;
54     }
55 
56     public static boolean willCleanupFastThreadLocals(Thread thread) {
57         return thread instanceof FastThreadLocalThread && ((FastThreadLocalThread)thread).willCleanupFastThreadLocals();
58     }
59 }

If you have read my previous write ThreadLocal source code analysis, and see which understand, very important point is that in the JDK ThreadLocal members of a ThreadLocalMap type in the Thread class, each thread maintains that a ThreadLocalMap, by ThreadLocalMap to ThreadLocal objects and generate the mapping relation; and here and JDK Similarly binding is InternalThreadLocalMap.

fastGet method:

1 private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
2    InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
3     if (threadLocalMap == null) {
4         thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
5     }
6 
7     return threadLocalMap;
8 }

There are also similar and the JDK ThreadLocal to determine whether members of threadLocalMap FastThreadLocalThread thread is null, if null, then create a InternalThreadLocalMap instance:

1 private InternalThreadLocalMap() {
2     super(newIndexedVariableTable());
3 }

First call newIndexedVariableTable method:

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

Create an array of size 32, and fills the entire array with UNSET this Object, and then call UnpaddedInternalThreadLocalMap construction, so indexedVariables members hold the array

Let's look at slowGet method:

 1 private static InternalThreadLocalMap slowGet() {
 2     ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = UnpaddedInternalThreadLocalMap.slowThreadLocalMap;
 3     InternalThreadLocalMap ret = (InternalThreadLocalMap)slowThreadLocalMap.get();
 4     if (ret == null) {
 5         ret = new InternalThreadLocalMap();
 6         slowThreadLocalMap.set(ret);
 7     }
 8 
 9     return ret;
10 }

It can be seen here, in fact, in order to improve efficiency, and there is no direct use of JDK ThreadLocal, but to the current non-binding FastThreadLocalThread thread a ThreadLocal <InternalThreadLocalMap> objects, avoid direct use of JDK ThreadLocal low efficiency.

Back to the set of methods FastThreadLocal, after obtaining the InternalThreadLocalMap to members of the current thread, call setKnownNotUnset method:

1 private boolean setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value) {
2     if (threadLocalMap.setIndexedVariable(this.index, value)) {
3         addToVariablesToRemove(threadLocalMap, this);
4         return true;
5     } else {
6         return false;
7     }
8 }

First call setIndexedVariable method InternalThreadLocalMap of:

 1 public boolean setIndexedVariable(int index, Object value) {
 2     Object[] lookup = this.indexedVariables;
 3     if (index < lookup.length) {
 4         Object oldValue = lookup[index];
 5         lookup[index] = value;
 6         return oldValue == UNSET;
 7     } else {
 8         this.expandIndexedVariableTableAndSet(index, value);
 9         return true;
10     }
11 }

Because the index is a constant can not be changed, so that there are two cases:
when indexedVariables greater than the length of the array index Object, directly on indexedVariables array subscript value for the location of index, return is equal oldValue UNSET, if not equal to UNSET , have been described over a set, linear replacement, if equal UNSET, but also for subsequent registerCleaner
when indexedVariables this length is less than or equal Object array index, expansion method call expandIndexedVariableTableAndSet

expandIndexedVariableTableAndSet方法:

 1 private void expandIndexedVariableTableAndSet(int index, Object value) {
 2     Object[] oldArray = this.indexedVariables;
 3     int oldCapacity = oldArray.length;
 4     int newCapacity = index | index >>> 1;
 5     newCapacity |= newCapacity >>> 2;
 6     newCapacity |= newCapacity >>> 4;
 7     newCapacity |= newCapacity >>> 8;
 8     newCapacity |= newCapacity >>> 16;
 9     ++newCapacity;
10     Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
11     Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
12     newArray[index] = value;
13     this.indexedVariables = newArray;
14 }

If HashMap read source words to the above-described arithmetic operation because the bit is not familiar with the value of this bit arithmetic newCapacity generated oldCapacity greater than the smallest integer power of two ( [] Java HashMap in tableSizeFor Method )

Then apply a newCapacity size array, the original contents of the array are copied to the new array, and the remaining portion is filled with UNSET, or on the value of the subscript index position, with a new array stored indexedVariables.

After setIndexedVariable establishment, setKnownNotUnset continue to call addToVariablesToRemove method:

 1 private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
 2     Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
 3     Set variablesToRemove;
 4     if (v != InternalThreadLocalMap.UNSET && v != null) {
 5         variablesToRemove = (Set)v;
 6     } else {
 7         variablesToRemove = Collections.newSetFromMap(new IdentityHashMap());
 8         threadLocalMap.setIndexedVariable(variablesToRemoveIndex, variablesToRemove);
 9     }
10 
11     variablesToRemove.add(variable);
12 }

The above mentioned variablesToRemoveIndex constant is 0, the call InternalThreadLocalMap indexedVariable method:

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

Since variablesToRemoveIndex identically equal to 0, where it is determined whether the Object indexedVariables array is empty, if empty, element 0 is returned, if not return to the UNSET

In addToVariablesToRemove, followed by the return value indexedVariables were judged,
judgment is not UNSET, and not equal to null, it indicates that the set off, and then just the return value strongly Switch Set type
if the above condition is not satisfied, to create a the IdentityHashMap, its packaging into Set assigned to variablesToRemove, then call setIndexedVariable method InternalThreadLocalMap, where it is not the same as above, the above is the value placed in the marked position index, and here on the Set index to 0 position.

See this, combined with the above, in fact, already have a general idea, at the beginning at the time set, the value is marked down on InternalThreadLocalMap the Object array index for the position, and then get here at index 0 Set, Description value is temporarily placed under the subscript index position, then the position is determined at index 0 are not Set, if so, remove the Set, the current object into a Set FastThreadLocal, then this is stored in the Set FastThreadLocal collection
then there is the following relationship:

Back to the set of methods FastThreadLocal, after setKnownNotUnset establishment, call registerCleaner method:

1 private void registerCleaner(InternalThreadLocalMap threadLocalMap) {
2     Thread current = Thread.currentThread();
3     if (!FastThreadLocalThread.willCleanupFastThreadLocals(current) && !threadLocalMap.isCleanerFlagSet(this.index)) {
4         threadLocalMap.setCleanerFlag(this.index);
5     }
6 }

willCleanupFastThreadLocals的返回值在前面FastThreadLocalThread的初始化时就确定了,看到isCleanerFlagSet方法:

1 public boolean isCleanerFlagSet(int index) {
2     return this.cleanerFlags != null && this.cleanerFlags.get(index);
3 }

cleanerFlags 是一个BitSet对象,在InternalThreadLocalMap初始化时是null,
若不是第一次的set操作,则根据index,获取index在BitSet对应位的值

这里使用BitSet,使其持有的位和indexedVariables这个Object数组形成了一一对应关系,每一位都是0和1代表当前indexedVariables的对应下标位置的使用情况,0表示没有使用对应UNSET,1则代表有value

在上面条件成立的情况下,调用setCleanerFlag方法:

1 public void setCleanerFlag(int index) {
2     if (this.cleanerFlags == null) {
3         this.cleanerFlags = new BitSet();
4     }
5 
6     this.cleanerFlags.set(index);
7 }

逻辑比较简单,判断cleanerFlags是否初始化,若没有,则立即初始化,再将cleanerFlags中对应index位的值设为1;

这里通过registerCleaner直接标记了所有set了value的下标可,为以后的removeAll 清除提高效率。

下来看FastThreadLocal的get方法:

 1 public final V get() {
 2     InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
 3     Object v = threadLocalMap.indexedVariable(this.index);
 4     if (v != InternalThreadLocalMap.UNSET) {
 5         return v;
 6     } else {
 7         V value = this.initialize(threadLocalMap);
 8         this.registerCleaner(threadLocalMap);
 9         return value;
10     }
11 }

和上面一样,先取得当前线程持有的InternalThreadLocalMap ,调用indexedVariable方法,根据当前FastThreadLocal的index定位,判断是否是UNSET(set过),若没有set过则和JDK一样调用initialize先set:

 1 private V initialize(InternalThreadLocalMap threadLocalMap) {
 2     Object v = null;
 3 
 4     try {
 5         v = this.initialValue();
 6     } catch (Exception var4) {
 7         PlatformDependent.throwException(var4);
 8     }
 9 
10     threadLocalMap.setIndexedVariable(this.index, v);
11     addToVariablesToRemove(threadLocalMap, this);
12     return v;
13 }

initialValue()方法就是对外提供的,需要手动覆盖:

1 protected V initialValue() throws Exception {
2     return null;
3 }

后面的操作就和set的逻辑一样。

 

remove方法:

1 public final void remove() {
2     this.remove(InternalThreadLocalMap.getIfSet());
3 }

getIfSet方法:

1 public static InternalThreadLocalMap getIfSet() {
2     Thread thread = Thread.currentThread();
3     return thread instanceof FastThreadLocalThread ? ((FastThreadLocalThread)thread).threadLocalMap() : (InternalThreadLocalMap)slowThreadLocalMap.get();
4 }

和上面的get方法思路相似,只不过在这里如果获取不到不会创建
然后调用remove重载:

 1 public final void remove(InternalThreadLocalMap threadLocalMap) {
 2     if (threadLocalMap != null) {
 3         Object v = threadLocalMap.removeIndexedVariable(this.index);
 4         removeFromVariablesToRemove(threadLocalMap, this);
 5         if (v != InternalThreadLocalMap.UNSET) {
 6             try {
 7                 this.onRemoval(v);
 8             } catch (Exception var4) {
 9                 PlatformDependent.throwException(var4);
10             }
11         }
12 
13     }
14 }

先检查threadLocalMap是否存在,若存在才进行后续操作:
调用removeIndexedVariable方法:

 1 public Object removeIndexedVariable(int index) {
 2     Object[] lookup = this.indexedVariables;
 3     if (index < lookup.length) {
 4         Object v = lookup[index];
 5         lookup[index] = UNSET;
 6         return v;
 7     } else {
 8         return UNSET;
 9     }
10 }

和之前的setIndexedVariable逻辑相似,只不过现在是把index位置的元素设置为UNSET

接着调用removeFromVariablesToRemove方法:

1 private static void removeFromVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal<?> variable) {
2     Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
3     if (v != InternalThreadLocalMap.UNSET && v != null) {
4         Set<FastThreadLocal<?>> variablesToRemove = (Set)v;
5         variablesToRemove.remove(variable);
6     }
7 }

之前说过variablesToRemoveIndex恒为0,在Object数组中下标为0存储的Set<FastThreadLocal<?>>集合(不为UNSET情况下),从集合中,将当前FastThreadLocal移除掉
最后调用了onRemoval方法,该方法需要由用户去覆盖:

1 protected void onRemoval(V value) throws Exception {
2 }


removeAll方法,是一个静态方法:

 1 public static void removeAll() {
 2     InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.getIfSet();
 3     if (threadLocalMap != null) {
 4         try {
 5             Object v = threadLocalMap.indexedVariable(variablesToRemoveIndex);
 6             if (v != null && v != InternalThreadLocalMap.UNSET) {
 7                 Set<FastThreadLocal<?>> variablesToRemove = (Set)v;
 8                 FastThreadLocal<?>[] variablesToRemoveArray = (FastThreadLocal[])variablesToRemove.toArray(new FastThreadLocal[0]);
 9                 FastThreadLocal[] var4 = variablesToRemoveArray;
10                 int var5 = variablesToRemoveArray.length;
11 
12                 for(int var6 = 0; var6 < var5; ++var6) {
13                     FastThreadLocal<?> tlv = var4[var6];
14                     tlv.remove(threadLocalMap);
15                 }
16             }
17         } finally {
18             InternalThreadLocalMap.remove();
19         }
20 
21     }
22 }

首先获取当前线程的InternalThreadLocalMap,若是存在继续后续操作:
通过indexedVariable方法,取出Object数组中下标为0的Set集合(如果不是UNSET情况下),将其转换为FastThreadLocal数组,遍历这个数组调用上面的remove方法。

FastThreadLocal源码分析到此结束。

 

Guess you like

Origin www.cnblogs.com/a526583280/p/10961801.html