Netty源码分析----FastThreadLocal-index生成机制详解

FastThreadLocal index 生成机制详解

问题背景

PooledByteBufAllocator 类的构造函数中:

threadCache = new PoolThreadLocalCache(useCacheForAllThreads);

这会调用 PoolThreadLocalCache 父类 FastThreadLocal 的构造函数:

public FastThreadLocal() {
    index = InternalThreadLocalMap.nextVariableIndex();
}

那么这个 index 是怎么来的呢?

核心机制解析

1. nextIndex 静态变量

UnpaddedInternalThreadLocalMap 类中定义了一个静态的 AtomicInteger

class UnpaddedInternalThreadLocalMap {
    static final AtomicInteger nextIndex = new AtomicInteger();
    // ...
}

关键点:

  • nextIndex 是一个静态变量,在整个 JVM 进程中全局共享
  • 使用 AtomicInteger 保证线程安全的自增操作
  • 初始值为 0(AtomicInteger 默认值)

2. nextVariableIndex() 方法

InternalThreadLocalMap 类中实现:

public static int nextVariableIndex() {
    int index = nextIndex.getAndIncrement();  // 原子性地获取当前值并自增
    if (index < 0) {
        nextIndex.decrementAndGet();
        throw new IllegalStateException("too many thread-local indexed variables");
    }
    return index;
}

工作流程:

  1. 调用 getAndIncrement() 原子性地获取当前值,并将 nextIndex 加 1
  2. 检查是否溢出(index < 0 表示超过 Integer.MAX_VALUE)
  3. 如果溢出,回退并抛出异常
  4. 返回获取到的 index 值

3. FastThreadLocal 构造函数

public class FastThreadLocal<V> {
    // 第一个 FastThreadLocal 实例会占用 index = 0
    private static final int variablesToRemoveIndex = InternalThreadLocalMap.nextVariableIndex();
    
    private final int index;  // 每个实例的唯一索引
    
    public FastThreadLocal() {
        index = InternalThreadLocalMap.nextVariableIndex();
    }
}

完整的 index 分配流程

示例场景

假设我们创建多个 FastThreadLocal 实例:

// 第一次:FastThreadLocal 类加载时
// variablesToRemoveIndex = nextIndex.getAndIncrement() = 0
// nextIndex 变为 1

// 创建第一个 FastThreadLocal 实例
FastThreadLocal<String> ftl1 = new FastThreadLocal<>();
// ftl1.index = nextIndex.getAndIncrement() = 1
// nextIndex 变为 2

// 创建第二个 FastThreadLocal 实例
FastThreadLocal<Integer> ftl2 = new FastThreadLocal<>();
// ftl2.index = nextIndex.getAndIncrement() = 2
// nextIndex 变为 3

// 创建 PoolThreadLocalCache(继承自 FastThreadLocal)
PoolThreadLocalCache cache = new PoolThreadLocalCache(true);
// cache.index = nextIndex.getAndIncrement() = 3
// nextIndex 变为 4

index 分配表

实例index 值说明
FastThreadLocal.variablesToRemoveIndex0类加载时分配,用于存储需要清理的 FastThreadLocal 列表
第一个 FastThreadLocal 实例1第一个用户创建的实例
第二个 FastThreadLocal 实例2第二个用户创建的实例
PoolThreadLocalCache 实例3PooledByteBufAllocator 中的 threadCache
依次递增

index 的作用

1. 作为数组索引

每个线程都有一个 InternalThreadLocalMap,其中包含一个 Object[] 数组:

class UnpaddedInternalThreadLocalMap {
    Object[] indexedVariables;  // 存储线程本地变量的数组
}

index 就是这个数组的索引位置:

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

public boolean setIndexedVariable(int index, Object value) {
    Object[] lookup = indexedVariables;
    if (index < lookup.length) {
        Object oldValue = lookup[index];
        lookup[index] = value;  // 使用 index 作为数组索引
        return oldValue == UNSET;
    } else {
        expandIndexedVariableTableAndSet(index, value);
        return true;
    }
}

2. 快速访问

通过 index 直接访问数组元素,时间复杂度为 O(1),比 JDK 的 ThreadLocal 使用哈希表更快。

关键设计要点

1. 全局唯一性

  • nextIndex 是静态变量,保证每个 FastThreadLocal 实例都有唯一的 index
  • 即使在不同线程中创建,index 也是全局递增的

2. 线程安全

  • 使用 AtomicInteger 保证多线程环境下的安全自增
  • 无需额外的同步机制

3. 空间换时间

  • 每个线程维护一个数组,空间开销较大
  • 但访问速度极快,适合高性能场景

4. 动态扩容

private void expandIndexedVariableTableAndSet(int index, Object value) {
    Object[] oldArray = indexedVariables;
    final int oldCapacity = oldArray.length;
    int newCapacity = index;
    // 使用位运算计算新容量(向上取到最近的 2 的幂次)
    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;
}

总结

FastThreadLocalindex 生成机制:

  1. 全局计数器:使用静态的 AtomicInteger nextIndex 作为全局计数器
  2. 原子自增:每次创建 FastThreadLocal 实例时,调用 getAndIncrement() 获取唯一索引
  3. 数组索引index 用作每个线程的 indexedVariables 数组的索引位置
  4. 高性能访问:通过数组索引直接访问,避免哈希计算和冲突处理
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值