Netty中InternalThreadLocalMap的作用

io.netty.util.internal.InternalThreadLocalMap 是 Netty 内部工具类,用于管理高效的线程局部存储(Thread-Local Storage)。它替代了 Java 标准库的 ThreadLocal,为 Netty 的高性能异步框架提供了优化的线程局部变量管理机制。该类在 Netty 的运行时环境中扮演关键角色,特别是在事件循环(EventLoop)、对象池(Recycler)、随机数生成和其他性能敏感场景中。

  • 作用
    • 提供高效的线程局部存储,管理每个线程的独立数据(如缓存、计数器、上下文)。
    • 优化 ThreadLocal 的性能,减少内存分配和访问开销。
    • 支持 Netty 的核心功能模块,如 FastThreadLocal、对象池(Recycler)、随机数生成(ThreadLocalRandom)、ChannelHandler 共享性检查等。

源码注释

/**
 * InternalThreadLocalMap 是 Netty 的线程局部存储管理类,用于高效存储线程特定数据。
 * 每个线程拥有一个唯一的 InternalThreadLocalMap 实例,通过 ThreadLocal 或 FastThreadLocalThread 访问。
 * 支持 FastThreadLocal、对象池、随机数生成等功能。
 */
public final class InternalThreadLocalMap extends UnpaddedInternalThreadLocalMap {
    /**
     * 慢路径的 ThreadLocal,用于普通线程存储 InternalThreadLocalMap。
     */
    private static final ThreadLocal<InternalThreadLocalMap> slowThreadLocalMap = 
        new ThreadLocal<InternalThreadLocalMap>();
    
    /**
     * 用于分配线程局部变量的索引,递增生成唯一索引。
     */
    private static final AtomicInteger nextIndex = new AtomicInteger();
    
    /**
     * 表示未设置的占位符对象。用于区分未设置值和值为null
     */
    public static final Object UNSET = new Object();
    
    /**
     * 存储线程局部变量的数组,使用索引访问。
     */
    private Object[] indexedVariables;
    
    // 核心字段
  
  //在 Netty 中,Future 对象可以添加多个监听器(FutureListener),当 Future 完成时,会通知所有添加的监听器。如果在监听器的 operationComplete 方法   中又触发了另一个 Future 的完成,并且这个新的 Future 也有监听器,就会形成递归调用。如果递归深度过深,就可能导致 StackOverflowError。
  //futureListenerStackDepth 字段用于记录当前线程中 Future 监听器通知的递归深度。Netty 通过配置一个最大递归深度(通过系统属性   io.netty.defaultPromise.maxListenerStackDepth 配置,默认值为 8),当递归深度超过这个最大值时,Netty 会将后续的监听器通知任务放到事件循环中异步执行,而不是继续在当前栈中递归调用,从而避免栈溢出。

    private int futureListenerStackDepth;
  //在 Netty 的本地传输中,当一个 LocalChannel 读取数据时,可能会触发一系列的操作,这些操作可能会递归调用读取逻辑。如果递归深度过深,就可能导致 StackOverflowError。localChannelReaderStackDepth 字段可以记录当前线程中 LocalChannel 读取操作的递归深度,当递归深度超过一定阈值时,可以采取相应的措施来避免栈溢出。
    private int localChannelReaderStackDepth; // Channel 读取栈深度
  //handlerSharableCache 把类和其对应的是否可共享的布尔值映射起来。当首次判断某个 ChannelHandler 类是否可共享时,会进行反射操作并把结果存入缓存;后续再判断同一类时,直接从缓存获取结果,无需再次反射,提高了判断效率。
    private Map<Class<?>, Boolean> handlerSharableCache; // ChannelHandler 共享缓存
    private IntegerHolder counterHashCode; // 计数器哈希码
    private ThreadLocalRandom random; // 线程局部随机数生成器
  //TypeParameterMatcher 用于判断一个对象是否是特定类型或其子类型,常用于处理消息类型匹配的场景。当需要判断某个对象是否为特定类型时,会使用 TypeParameterMatcher 来进行匹配。
    private Map<Class<?>, TypeParameterMatcher> typeParameterMatcherGetCache; // 类型匹配缓存
    private Map<Class<?>, Map<String, TypeParameterMatcher>> typeParameterMatcherFindCache; // 类型查找缓存

    /**
     * 获取当前线程的 InternalThreadLocalMap 实例。
     * @return 当前线程的 InternalThreadLocalMap
     */
    public static InternalThreadLocalMap get() {
        Thread thread = Thread.currentThread();
        if (thread instanceof FastThreadLocalThread) {
            return fastGet((FastThreadLocalThread) thread);
        } else {
            return slowGet();
        }
    }

    /**
     * 适用于 FastThreadLocalThread 类型的线程。FastThreadLocalThread 是 Netty 自定义的线程类,它内部维护了一个 InternalThreadLocalMap 实      *  例,通过 threadLocalMap() 方法可以直接获取该实例。
     */
    private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
        InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
        if (threadLocalMap == null) {
            thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
        }
        return threadLocalMap;
    }

    /**
     * 适用于普通的 Thread 类型的线程。对于普通线程,没有内置的 InternalThreadLocalMap 实例,因此需要使用 Java 标准的 ThreadLocal 来存储和获取      * InternalThreadLocalMap 实例。
     */
    private static InternalThreadLocalMap slowGet() {
        InternalThreadLocalMap ret = slowThreadLocalMap.get();
        if (ret == null) {
            ret = new InternalThreadLocalMap();
            slowThreadLocalMap.set(ret);
        }
        return ret;
    }

    /**
     * 构造函数,初始化 indexedVariables 数组。
     */
    private InternalThreadLocalMap() {
        super(newIndexedVariableTable());
    }

    /**
     * 创建初始大小为 32 的线程局部变量数组,填充 UNSET。
     * @return 初始化后的数组
     */
    private static Object[] newIndexedVariableTable() {
        Object[] array = new Object[32];
        Arrays.fill(array, UNSET);
        return array;
    }

    /**
     * 分配一个新的线程局部变量索引。
     * @return 唯一索引
     * @throws IllegalStateException 如果索引溢出
     */
    public static int nextVariableIndex() {
        int index = nextIndex.getAndIncrement();
        if (index < 0) {
            nextIndex.decrementAndGet();
            throw new IllegalStateException("too many thread-local indexed variables");
        }
        return index;
    }

    /**
     * 获取指定索引的线程局部变量值。
     * @param index 变量索引
     * @return 变量值,或 UNSET 如果未设置
     */
    public Object indexedVariable(int index) {
        Object[] lookup = indexedVariables;
        return index < lookup.length ? lookup[index] : UNSET;
    }

    /**
     * 设置指定索引的线程局部变量值。
     * @param index 变量索引
     * @param value 要设置的值
     * @return true 如果是首次设置,false 如果覆盖已有值
     */
    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;
        }
    }

    /**
     * 扩展 indexedVariables 数组以支持更大的索引,并设置值。
     * @param index 变量索引
     * @param value 要设置的值
     */
    private void expandIndexedVariableTableAndSet(int index, Object value) {
        Object[] oldArray = indexedVariables;
        int oldCapacity = oldArray.length;
        int newCapacity = index;
        newCapacity |= newCapacity >>> 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;
        indexedVariables = newArray;
    }
}

3. 作用与功能

InternalThreadLocalMap 是 Netty 的线程局部存储核心,其主要作用包括:

  1. 高效线程局部存储

    • 使用数组(indexedVariables)存储线程局部变量,通过索引访问,性能优于 Java ThreadLocalHashMap 实现。
    • 支持 FastThreadLocal,Netty 的高性能 ThreadLocal 替代品,通过 nextVariableIndex 分配唯一索引。
  2. 支持 Netty 功能模块

    • 对象池(Recycler):存储对象池的上下文数据,减少对象分配开销(如 ByteBuf 缓存)。
    • 随机数生成random 字段提供线程局部的 ThreadLocalRandom 实例。
    • ChannelHandler 共享性handlerSharableCache 缓存 @Sharable 状态,优化 ChannelPipeline 处理。
    • 类型匹配缓存typeParameterMatcherGetCachetypeParameterMatcherFindCache 减少反射开销。
    • 栈深度跟踪futureListenerStackDepthlocalChannelReaderStackDepth 防止递归调用溢出。
  3. 性能优化

    • 快速路径:对于 FastThreadLocalThread(Netty 自定义线程类),直接存储和访问 InternalThreadLocalMap,避免 ThreadLocal 的开销。
    • 慢路径:对于普通线程,使用 ThreadLocal 存储,保持兼容性。
    • 数组存储:初始大小为 32 的数组,动态扩展,减少哈希操作。
  4. 线程隔离

    • 确保每个线程的 InternalThreadLocalMap 独立,避免数据干扰。
    • 特别适合 Netty 的单线程 EventLoop 模型。

4. 关键方法解析

以下是 InternalThreadLocalMap 的核心方法及其作用:

4.1 get
  • 签名public static InternalThreadLocalMap get()
  • 作用:获取当前线程的 InternalThreadLocalMap 实例。
  • 逻辑
    • 检查线程是否为 FastThreadLocalThread
      • 如果是,调用 fastGet 直接访问。
      • 否则,调用 slowGet 使用 ThreadLocal
    • 如果实例不存在,创建并存储。
  • 使用场景FastThreadLocal 和其他模块通过 get 获取线程局部存储。
4.2 nextVariableIndex
  • 签名public static int nextVariableIndex()
  • 作用:分配唯一索引,用于 FastThreadLocal 变量存储。
  • 逻辑
    • 使用 AtomicInteger 递增生成索引。
    • 防止索引溢出(负数时抛异常)。
4.3 indexedVariable
  • 签名public Object indexedVariable(int index)
  • 作用:获取指定索引的线程局部变量值。
  • 逻辑
    • 如果索引在数组范围内,返回值;否则返回 UNSET
4.4 setIndexedVariable
  • 签名public boolean setIndexedVariable(int index, Object value)
  • 作用:设置指定索引的线程局部变量值。
  • 逻辑
    • 如果索引在数组范围内,设置值并返回是否首次设置。
    • 如果索引超出范围,扩展数组并设置值。
4.5 expandIndexedVariableTableAndSet
  • 签名private void expandIndexedVariableTableAndSet(int index, Object value)
  • 作用:扩展 indexedVariables 数组以支持更大索引。
  • 逻辑
    • 使用位运算计算新容量(接近 2 的幂)。
    • 复制旧数组,填充新部分为 UNSET,设置指定索引的值。

5. 与 EventLoop 的关系

InternalThreadLocalMap 在 Netty 的事件循环(EventLoop)模型中发挥重要作用:

  1. 线程隔离

    • 每个 EventLoop(如 NioEventLoop)绑定一个线程,InternalThreadLocalMap 为该线程提供独立的存储空间。
    • 示例:NioEventLoop 使用 InternalThreadLocalMap 存储事件循环的上下文数据。
  2. 性能优化

    • EventLoop 线程通常是 FastThreadLocalThread,通过 fastGet 直接访问 InternalThreadLocalMap,减少开销。
    • 示例:在 NioEventLoop#run 方法中,FastThreadLocal 可能用于存储临时状态。
  3. 支持异步操作

    • InternalThreadLocalMap 的字段(如 futureListenerStackDepth)用于跟踪 ChannelFuture 监听器的调用栈深度,防止递归溢出。
    • 示例:在 ChannelPipeline 的事件处理中,handlerSharableCache 优化 ChannelHandler 的共享性检查。
  4. 对象池支持

    • EventLoop 线程使用 InternalThreadLocalMap 管理对象池(如 Recycler),提高 ByteBuf 等对象的复用效率。

6. 使用场景

InternalThreadLocalMap 在 Netty 中的具体应用包括:

  1. FastThreadLocal 存储

    • 支持 FastThreadLocal,Netty 的高性能 ThreadLocal 实现。
    • 示例:ByteBufRecycler 使用 FastThreadLocal 存储对象池上下文。
  2. 对象池(Recycler)

    • 管理线程局部的对象池实例,减少内存分配。
    • 示例:PooledByteBufAllocator 使用 InternalThreadLocalMap 缓存 ByteBuf
  3. 随机数生成

    • random 字段提供线程局部的 ThreadLocalRandom 实例。
    • 示例:负载均衡或随机选择 EventLoop
  4. ChannelHandler 共享性

    • handlerSharableCache 缓存 ChannelHandler@Sharable 状态。
    • 示例:ChannelPipeline 检查 ChannelHandler 是否可共享。
  5. 类型匹配优化

    • typeParameterMatcherGetCachetypeParameterMatcherFindCache 缓存类型匹配结果,减少反射开销。
    • 示例:ChannelHandler 的类型检查。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值