13.InternalThreadLocalMap

UnpaddedInternalThreadLocalMap

InternalThreadLocalMap继承自UnpaddedInternalThreadLocalMap所以先来看一下这个类.

// 内部数据结构, 用于存储 Netty 和所有 FastThreadLocal 的线程局部变量.
class UnpaddedInternalThreadLocalMap {

    // 如果在 Thread 中使用 FastThreadLocal, 则实际上使用 ThreadLocal 存放资源.
    static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = new ThreadLocal<InternalThreadLocalMap>();

    // 资源索引, 每一个 FastThreadLocal 对象都会有对应的ID, 即通过 nextIndex 自增得到.
    static final AtomicInteger nextIndex = new AtomicInteger();

    // 该值是在 InternalThreadLocalMap 构造方法中初始化, 数组默认大小 32, 并且数组中的所有值初始化为 InternalThreadLocalMap.UNSET.
    Object[] indexedVariables;

    // 下面这些变量在 InternalThreadLocalMap 中都会说道.

    // 调用深度
    int futureListenerStackDepth;
    int localChannelReaderStackDepth;
    Map<Class<?>, Boolean> handlerSharableCache;
    IntegerHolder counterHashCode;
    ThreadLocalRandom random;
    Map<Class<?>, TypeParameterMatcher> typeParameterMatcherGetCache;
    Map<Class<?>, Map<String, TypeParameterMatcher>> typeParameterMatcherFindCache;

    StringBuilder stringBuilder;
    // 在 charsetEncoderCache() 方法调用时如果为 null 就会创建 IdentityHashMap<Charset, CharsetEncoder> 实例.
    Map<Charset, CharsetEncoder> charsetEncoderCache;
    // 在 charsetDecoderCache() 方法调用时如果为 null 就会创建 IdentityHashMap<Charset, CharsetDecoder> 实例.
    Map<Charset, CharsetDecoder> charsetDecoderCache;

    // 调用 arrayList() 方法时会使用默认参数 8 来调用 arrayList(int) 方法, 
    // 如果 arrayList 属性为 null 则会创建 ArrayList<Object>(minCapacity) 实例;
    // 否则就会清空 arrayList 集合, 并调用 ensureCapacity 方法.
    ArrayList<Object> arrayList;

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

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;
    private static final int STRING_BUILDER_MAX_SIZE;
    private static final int HANDLER_SHARABLE_CACHE_INITIAL_CAPACITY = 4;
    private static final int INDEXED_VARIABLE_TABLE_INITIAL_SIZE = 32;

    public static final Object UNSET = new Object();

    private BitSet cleanerFlags;

    // 指定 stringBuilder 的初始化大小和最大大小.
    static {
        STRING_BUILDER_INITIAL_SIZE =
                SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.initialSize", 1024);
        logger.debug("-Dio.netty.threadLocalMap.stringBuilder.initialSize: {}", STRING_BUILDER_INITIAL_SIZE);

        STRING_BUILDER_MAX_SIZE = SystemPropertyUtil.getInt("io.netty.threadLocalMap.stringBuilder.maxSize", 1024 * 4);
        logger.debug("-Dio.netty.threadLocalMap.stringBuilder.maxSize: {}", STRING_BUILDER_MAX_SIZE);
    }

    // 通过当前线程的类型获取线程值. 如果没有设置则返回 null.
    public static InternalThreadLocalMap getIfSet() {
        Thread thread = Thread.currentThread();
        if (thread instanceof FastThreadLocalThread) {
            // 由于 FastThreadLocalThread 中直接绑定了 InternalThreadLocalMap 实例, 所以可以直接获取.
            return ((FastThreadLocalThread) thread).threadLocalMap();
        }
        // 这里说明该线程不是 FastThreadLocalThread 类型或子类.
        // 通过 jdk 的 ThreadLocal 获取当前线程的值.
        return slowThreadLocalMap.get();
    }

    // 如果当前线程没有设置值, 则会创建一个新的 InternalThreadLocalMap 实例后设置并返回.
    public static InternalThreadLocalMap get() {
        Thread thread = Thread.currentThread();
        if (thread instanceof FastThreadLocalThread) {
            // 如果为 null 则会调用 FastThreadLocalThread#setThreadLocalMap 方法添加新实例.
            return fastGet((FastThreadLocalThread) thread);
        } else {
            // 如果为 null 则会调用 slowThreadLocalMap.set 方法添加新实例.
            return slowGet();
        }
    }

    //  fastGet 和 slowGet 代码就不复制了.

    // 删除当前线程的值, 没啥好说的就是删除 InternalThreadLocalMap 实例.
    public static void remove() {
        Thread thread = Thread.currentThread();
        if (thread instanceof FastThreadLocalThread) {
            ((FastThreadLocalThread) thread).setThreadLocalMap(null);
        } else {
            slowThreadLocalMap.remove();
        }
    }

    // 这里就是获取一个唯一ID.
    public static int nextVariableIndex() {
        // getAndIncrement 方法类似于 i++;
        int index = nextIndex.getAndIncrement();
        // 如果为负数说明已经 ++ 到超过 Int 类型的最大值(2147483647)
        if (index < 0) {
            nextIndex.decrementAndGet();
            throw new IllegalStateException("too many thread-local indexed variables");
        }
        return index;
    }

    // 当前 ID 的上一个.
    public static int lastVariableIndex() {
        return nextIndex.get() - 1;
    }

    // 根据 FastThreadLocal 初始化的 index, 确定其在资源列表中的位置, 后续查询资源就可以根据索引快速确定位置.
    public boolean setIndexedVariable(int index, Object value) {
        Object[] lookup = indexedVariables; // 当前的Object[]对象
        if (index < lookup.length) { // 检查当前的索引是否超过了Object数组的长度
            Object oldValue = lookup[index]; // 获取当前的值
            lookup[index] = value; // 将新值设置进去
            return oldValue == UNSET; // 判断之前的值是不是占位符,如果是则代表新增,不是代表更新
        } else {
            expandIndexedVariableTableAndSet(index, value); // 如果超过了,则需要进行扩容
            return true;
        }
    }

    private void expandIndexedVariableTableAndSet(int index, Object value) { // 扩容逻辑
        Object[] oldArray = indexedVariables; // 当前的Object数组
        final int oldCapacity = oldArray.length; // 当前的长度
        int newCapacity = index;  // index是这个FastThreadLocal对应的索引值
        newCapacity |= newCapacity >>>  1;
        newCapacity |= newCapacity >>>  2;
        newCapacity |= newCapacity >>>  4;
        newCapacity |= newCapacity >>>  8;
        newCapacity |= newCapacity >>> 16;
        newCapacity ++; // 这段其实也很有意思, 其实是为了计算新的数组的容量, 会变成下一个档位的2的次方大小, 
                        // 比如 1->2 2->4 3->4 4->8 5->8 6->8 7->8 8->16 18->32

        Object[] newArray = Arrays.copyOf(oldArray, newCapacity); // 将老的数据往新的数组进行拷贝
        Arrays.fill(newArray, oldCapacity, newArray.length, UNSET); // 将新的多出来的部分,设置占位符
        newArray[index] = value; // 将index对应的值设置完
        indexedVariables = newArray; // 将新的数组提升成Map中的indexedVariables
    }

    // 根据当前的 Thread 索引, 获取 Object.
    // 这个是一次性的, 对应的线程数据会被设置为 UNSET.
    public Object removeIndexedVariable(int index) {
        Object[] lookup = indexedVariables;
        if (index < lookup.length) {
            Object v = lookup[index];
            lookup[index] = UNSET;
            return v;
        } else {
            return UNSET;
        }
    }
}

InternalThreadLocalMap 使用数组来管理每个线程中的变量.

参考文章

Netty源码分析--FastThreadLocal分析(十)

posted @ 2020-08-24 13:31  scikstack  阅读(624)  评论(0编辑  收藏  举报