InternalThreadLocalMap
InternalThreadLocalMap
是 Netty 中一个非常核心的内部工具类,是一个最终类,用于存储 Netty 和所有 FastThreadLocal
的线程本地变量。
它为 Netty 自身的组件以及用户定义的 FastThreadLocal
变量提供了一个高性能的线程本地存储机制。它的设计目标是优化标准 java.lang.ThreadLocal
在高并发和大量线程本地变量场景下的性能。
数组替代 ThreadLocalMap
: JDK 的 ThreadLocal
内部使用一个 ThreadLocalMap
(基于哈希表) 来存储每个线程的本地变量。当 ThreadLocal
实例很多时,哈希冲突和查找开销可能会增加。InternalThreadLocalMap
则主要使用一个对象数组Object[ ] (indexedVariables
) 来存储这些变量。每个 FastThreadLocal
实例在创建时都会获得一个唯一的、全局递增的索引 (index
),这个索引直接对应到 indexedVariables
数组的下标。这样,获取线程本地变量就变成了简单的数组访问操作,速度非常快。
FastThreadLocalThread
优化: Netty 引入了 FastThreadLocalThread
。如果当前线程是 FastThreadLocalThread
的实例,那么 InternalThreadLocalMap
实例会直接作为该线程的一个成员变量存在,访问速度更快,避免了 ThreadLocal.get()
的开销。
兼容普通线程: 对于非 FastThreadLocalThread
的普通线程,InternalThreadLocalMap
会退化为使用一个标准的 ThreadLocal
(slowThreadLocalMap
) 来存储其实例,保证了兼容性。
预定义和缓存常用对象: InternalThreadLocalMap
内部还预定义了一些 Netty 核心组件会频繁使用的线程本地对象,例如 StringBuilder
、CharsetEncoder
/CharsetDecoder
缓存、ArrayList
等。通过复用这些对象,可以减少对象创建和垃圾回收的开销。
private static final ThreadLocal slowThreadLocalMap = new ThreadLocal<>();
private static final AtomicInteger nextIndex = new AtomicInteger();
public static final int VARIABLES_TO_REMOVE_INDEX = nextVariableIndex();
private static final int DEFAULT_ARRAY_LIST_INITIAL_CAPACITY = 8;
private static final int ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD = 1 << 30;
private static final int ARRAY_LIST_CAPACITY_MAX_SIZE = Integer.MAX_VALUE - 8;
private static final int HANDLER_SHARABLE_CACHE_INITIAL_CAPACITY = 4;
private static final int INDEXED_VARIABLE_TABLE_INITIAL_SIZE = 32;
private static final int STRING_BUILDER_INITIAL_SIZE;
private static final int STRING_BUILDER_MAX_SIZE;
private static final InternalLogger logger;
public static final Object UNSET = new Object();
slowThreadLocalMap
:普通线程使用的 ThreadLocal
,用于存储 InternalThreadLocalMap
实例。nextIndex
:原子整数,用于生成下一个变量索引。VARIABLES_TO_REMOVE_INDEX
:需要移除的变量的索引。STRING_BUILDER_INITIAL_SIZE
和 STRING_BUILDER_MAX_SIZE
:StringBuilder
的初始大小和最大大小,从系统属性获取。logger
:日志记录器。UNSET
:表示变量未设置的特殊对象。InternalThreadLocalMap.java
private Object[] indexedVariables;
private int futureListenerStackDepth;
private int localChannelReaderStackDepth;
private Map, Boolean> handlerSharableCache;
private Map, TypeParameterMatcher> typeParameterMatcherGetCache;
private Map, Map> typeParameterMatcherFindCache;
private StringBuilder stringBuilder;
private Map charsetEncoderCache;
private Map charsetDecoderCache;
private ArrayList
indexedVariables
:用于存储线程本地变量的数组。futureListenerStackDepth
和 localChannelReaderStackDepth
:分别表示未来监听器栈深度和本地通道读取器栈深度。stringBuilder
:线程本地的 StringBuilder
。charsetEncoderCache
和 charsetDecoderCache
:字符集编码和解码器缓存。arrayList
:线程本地的 ArrayList
。cleanerFlags
:用于标记清理标志的 BitSet
。getIfSet()
和 get()
InternalThreadLocalMap.java
public static InternalThreadLocalMap getIfSet() {
Thread thread = Thread.currentThread();
if (thread instanceof FastThreadLocalThread) {
return ((FastThreadLocalThread) thread).threadLocalMap();
}
return slowThreadLocalMap.get();
}
public static InternalThreadLocalMap get() {
Thread thread = Thread.currentThread();
if (thread instanceof FastThreadLocalThread) {
return fastGet((FastThreadLocalThread) thread);
} else {
return slowGet();
}
}
getIfSet()
:获取当前线程的 InternalThreadLocalMap
实例,如果不存在则返回 null
。get()
:获取当前线程的 InternalThreadLocalMap
实例,如果不存在则创建一个新的实例。fastGet()
和 slowGet()
InternalThreadLocalMap.java
private static InternalThreadLocalMap fastGet(FastThreadLocalThread thread) {
InternalThreadLocalMap threadLocalMap = thread.threadLocalMap();
if (threadLocalMap == null) {
thread.setThreadLocalMap(threadLocalMap = new InternalThreadLocalMap());
}
return threadLocalMap;
}
private static InternalThreadLocalMap slowGet() {
InternalThreadLocalMap ret = slowThreadLocalMap.get();
if (ret == null) {
ret = new InternalThreadLocalMap();
slowThreadLocalMap.set(ret);
}
return ret;
}
fastGet()
:为 FastThreadLocalThread
快速获取或创建 InternalThreadLocalMap
实例。slowGet()
:为普通线程获取或创建 InternalThreadLocalMap
实例。setIndexedVariable()
和 getAndSetIndexedVariable()
InternalThreadLocalMap.java
public boolean setIndexedVariable(int index, Object value) {
return getAndSetIndexedVariable(index, value) == UNSET;
}
public Object getAndSetIndexedVariable(int index, Object value) {
Object[] lookup = indexedVariables;
if (index < lookup.length) {
Object oldValue = lookup[index];
lookup[index] = value;
return oldValue;
}
expandIndexedVariableTableAndSet(index, value);
return UNSET;
}
setIndexedVariable()
:设置指定索引的线程本地变量,如果是新变量则返回 true
。getAndSetIndexedVariable()
:设置指定索引的线程本地变量,并返回旧值。如果是新变量则返回 UNSET
。expandIndexedVariableTableAndSet()
InternalThreadLocalMap.java
private void expandIndexedVariableTableAndSet(int index, Object value) {
Object[] oldArray = indexedVariables;
final int oldCapacity = oldArray.length;
int newCapacity;
if (index < ARRAY_LIST_CAPACITY_EXPAND_THRESHOLD) {
newCapacity = index;
newCapacity |= newCapacity >>> 1;
newCapacity |= newCapacity >>> 2;
newCapacity |= newCapacity >>> 4;
newCapacity |= newCapacity >>> 8;
newCapacity |= newCapacity >>> 16;
newCapacity ++;
} else {
newCapacity = ARRAY_LIST_CAPACITY_MAX_SIZE;
}
Object[] newArray = Arrays.copyOf(oldArray, newCapacity);
Arrays.fill(newArray, oldCapacity, newArray.length, UNSET);
newArray[index] = value;
indexedVariables = newArray;
}
该方法用于扩展 indexedVariables
数组的容量,并设置指定索引的变量值。
UNSET
对象: 这是一个特殊的静态对象,用于表示 indexedVariables
数组中某个索引位置尚未被设置,或者已被移除。这比使用 null
更好,因为 null
本身可能是一个合法的线程本地变量值。
nextVariableIndex()
和 index
: 每个 FastThreadLocal
实例在构造时都会调用 InternalThreadLocalMap.nextVariableIndex()
来获取一个全局唯一的整数索引。这个索引直接用于在 InternalThreadLocalMap
的 indexedVariables
数组中定位该 FastThreadLocal
变量的值。
数组扩容 (expandIndexedVariableTableAndSet
): 当一个 FastThreadLocal
的索引超出了当前 indexedVariables
数组的长度时,会触发数组扩容。扩容策略是找到一个大于等于当前索引的最小的2的幂次方作为新容量(有一个上限 ARRAY_LIST_CAPACITY_MAX_SIZE
)。新数组会复制旧数组的内容,并将新增的部分用 UNSET
对象填充。
VARIABLES_TO_REMOVE_INDEX
: 这是一个特殊的索引,其对应的值是一个 Set
。这个集合用于存储那些需要被清理的 FastThreadLocal
实例。当调用 FastThreadLocal.removeAll()
或 FastThreadLocalThread
结束时,会遍历这个集合并移除相应的线程本地变量,防止内存泄漏。
预定义对象的复用:
stringBuilder()
: 提供一个可复用的 StringBuilder
。每次获取时,如果已存在,会清空其内容并检查容量是否过大。charsetEncoderCache()
/ charsetDecoderCache()
: 提供 CharsetEncoder
和 CharsetDecoder
的缓存,使用 IdentityHashMap
是因为 Charset
对象通常是单例的,可以安全地用作键。arrayList()
: 提供一个可复用的 ArrayList
。handlerSharableCache()
: 使用 WeakHashMap
缓存 ChannelHandler
是否标记为 @Sharable
。WeakHashMap
的键是弱引用,当 Class
对象不再被其他地方强引用时,可以被垃圾回收,从而避免内存泄漏。UnpaddedInternalThreadLocalMap
:UnpaddedInternalThreadLocalMap.java
// ... existing code ...
/**
* @deprecated This class will be removed in the future.
*/
class UnpaddedInternalThreadLocalMap {
// We cannot remove this in 4.1 because it could break compatibility.
}
这个父类目前是空的,并且被标记为 @deprecated
。它的存在主要是为了向后兼容。在早期版本中,InternalThreadLocalMap
可能包含一些用于避免伪共享 (false sharing) 的填充字段 (padding fields)。这些字段后来被移到了 UnpaddedInternalThreadLocalMap
中,然后 InternalThreadLocalMap
再继承它。
现在这些填充字段 (rp1
到 rp8
) 也被标记为 @deprecated
,并直接放在 InternalThreadLocalMap
中,说明 Netty 团队可能认为现代 JVM 和硬件对伪共享的处理已经足够好,或者有其他更有效的避免方式。
InternalThreadLocalMap
通过使用基于索引的数组存储、针对 FastThreadLocalThread
的优化以及预定义对象的复用,为 Netty 提供了一个比标准 ThreadLocal
更高效的线程本地存储方案。它的设计精巧,充分考虑了性能、内存使用和兼容性。理解其内部机制对于深入理解 Netty 的高性能特性非常有帮助。
FastThreadLocal
FastThreadLocal
是 Netty 中对 Java 标准 ThreadLocal
的一种特殊优化实现。当在 FastThreadLocalThread
类型的线程中使用时,它能提供比标准 ThreadLocal
更高的访问性能。
下面是对其类结构和核心源码实现的分析:
:表示线程局部变量的类型。ThreadLocal
使用哈希表定位变量不同,FastThreadLocal
为每个实例分配一个唯一的整数索引 (index
)。在 FastThreadLocalThread
中,这些变量存储在一个数组 (InternalThreadLocalMap.indexedVariables
) 中,通过索引直接访问,避免了哈希计算和潜在的哈希冲突,从而提高性能。FastThreadLocalThread
线程中使用,FastThreadLocal
会回退到类似标准 ThreadLocal
的机制(通过 InternalThreadLocalMap
的慢速路径)。核心成员变量
private final int index;
FastThreadLocal
最关键的实例变量。FastThreadLocal
对象构造时,通过 InternalThreadLocalMap.nextVariableIndex()
分配一个全局唯一的、自增的整数。index
直接用作 InternalThreadLocalMap
内部数组的下标,用于存取该线程的此 FastThreadLocal
变量的值。构造函数
public FastThreadLocal()
index
字段:index = InternalThreadLocalMap.nextVariableIndex();
public final V get()
FastThreadLocal
变量的值。InternalThreadLocalMap threadLocalMap = InternalThreadLocalMap.get();
: 获取当前线程关联的 InternalThreadLocalMap
。如果是 FastThreadLocalThread
,直接返回其成员;否则,通过一个静态的 ThreadLocal
获取。Object v = threadLocalMap.indexedVariable(index);
: 使用 index
从 InternalThreadLocalMap
的内部数组中获取值。v != InternalThreadLocalMap.UNSET
( UNSET
是一个特殊静态对象,表示未设置或已移除),则直接转换并返回 v
。v == UNSET
,则调用 initialize(threadLocalMap)
进行初始化。private V initialize(InternalThreadLocalMap threadLocalMap)
get()
发现变量未初始化时调用。v = initialValue();
: 调用用户可覆盖的 protected V initialValue() throws Exception
方法获取初始值。默认返回 null
。v
不能是 InternalThreadLocalMap.UNSET
。threadLocalMap.setIndexedVariable(index, v);
: 将初始值存入 InternalThreadLocalMap
的 index
位置。addToVariablesToRemove(threadLocalMap, this);
: 将当前 FastThreadLocal
实例添加到一个集合中,该集合由 InternalThreadLocalMap
维护,用于 removeAll()
操作。v
。protected V initialValue() throws Exception
null
的初始值。默认返回 null
。public final void set(V value)
FastThreadLocal
变量的值。public V getAndSet(V value)
。public V getAndSet(V value)
value == InternalThreadLocalMap.UNSET
,则视为移除操作,调用 removeAndGet(InternalThreadLocalMap.getIfSet())
。InternalThreadLocalMap
,然后调用 setKnownNotUnset(threadLocalMap, value)
。private V setKnownNotUnset(InternalThreadLocalMap threadLocalMap, V value)
:
V old = (V) threadLocalMap.getAndSetIndexedVariable(index, value);
: 在 InternalThreadLocalMap
中设置新值并获取旧值。old == UNSET
(表示之前未设置过),则调用 addToVariablesToRemove(threadLocalMap, this);
并返回 null
(因为逻辑上的旧值是“未设置”)。old
。public final void remove()
FastThreadLocal
变量。remove(InternalThreadLocalMap.getIfSet())
。private V removeAndGet(InternalThreadLocalMap threadLocalMap)
:
threadLocalMap
为 null
,返回 null
。Object v = threadLocalMap.removeIndexedVariable(index);
: 从 InternalThreadLocalMap
中移除 index
位置的变量,并将其标记为 UNSET
。v != UNSET
(表示确实移除了一个有效值):
removeFromVariablesToRemove(threadLocalMap, this);
: 从待移除集合中移除当前实例。onRemoval((V) v);
: 调用用户可覆盖的 protected void onRemoval(V value) throws Exception
方法,用于资源清理。v
。v == UNSET
,返回 null
。protected void onRemoval(V value) throws Exception
remove()
方法被移除时调用。子类可以覆盖此方法以执行必要的清理操作。默认无操作。public static void removeAll()
FastThreadLocal
变量。InternalThreadLocalMap
。map
中获取之前通过 addToVariablesToRemove
注册的 FastThreadLocal
实例集合 (存储在 VARIABLES_TO_REMOVE_INDEX
特殊索引处)。FastThreadLocal
实例调用其 remove(threadLocalMap)
方法。InternalThreadLocalMap.remove()
来清理 InternalThreadLocalMap
本身(如果是慢速路径,会移除 ThreadLocal
中的 InternalThreadLocalMap
)。public static int size()
FastThreadLocal
变量的数量。通过 InternalThreadLocalMap.getIfSet().size()
实现。public static void destroy()
FastThreadLocalThread
的 InternalThreadLocalMap
存储机制(即清除静态 ThreadLocal
)。主要用于容器环境下防止内存泄漏。private static void addToVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal> variable)
FastThreadLocal
实例添加到一个由 InternalThreadLocalMap
管理的 Set
中。这个 Set
存储在 threadLocalMap
的 VARIABLES_TO_REMOVE_INDEX
位置。此 Set
用于 removeAll()
操作。private static void removeFromVariablesToRemove(InternalThreadLocalMap threadLocalMap, FastThreadLocal> variable)
Set
中移除一个 FastThreadLocal
实例。InternalThreadLocalMap
和 FastThreadLocalThread
的关系InternalThreadLocalMap
: 是实际存储线程局部变量的容器。它内部有一个 Object[] indexedVariables
数组,FastThreadLocal
的 index
就是这个数组的下标。FastThreadLocalThread
: 这种类型的线程直接拥有一个 InternalThreadLocalMap
实例作为其成员变量。当 FastThreadLocal
在这种线程上操作时,可以直接访问这个 InternalThreadLocalMap
,并通过索引快速读写,这是“快速路径”。Thread
: 对于非 FastThreadLocalThread
,InternalThreadLocalMap
实例通过一个静态的 ThreadLocal
(名为 slowThreadLocalMap
在 InternalThreadLocalMap
类中) 来存储和获取,这是“慢速路径”,但功能上等价。FastThreadLocal
通过为每个实例分配一个唯一的数组索引,并结合 FastThreadLocalThread
直接持有 InternalThreadLocalMap
的设计,实现了对线程局部变量的 O(1) 访问。这在频繁访问线程局部变量的场景下(如Netty的事件循环中)能带来显著的性能提升。同时,它也提供了与标准 ThreadLocal
类似的回调方法 (initialValue
, onRemoval
) 和管理方法 (removeAll
, destroy
)。
FastThreadLocalThread
我们来深入分析 FastThreadLocalThread
类的结构和实现源码。这个类是 Netty 高性能线程本地存储 (FastThreadLocal
) 的核心组件之一。
FastThreadLocalThread.java
public class FastThreadLocalThread extends Thread {
// 日志记录器
private static final InternalLogger logger = ...;
// 用于存储被当作 FastThreadLocalThread 处理的线程 ID 数组
private static final AtomicReference fallbackThreads = new AtomicReference<>(null);
// 标记是否会在 run() 完成后清理 FastThreadLocal
private final boolean cleanupFastThreadLocals;
// 存储线程本地变量的核心数据结构
private InternalThreadLocalMap threadLocalMap;
// 多个构造函数...
// 核心方法...
}
关键成员分析
threadLocalMap
(核心成员)
InternalThreadLocalMap
FastThreadLocal
变量threadLocalMap()
和 setThreadLocalMap()
方法cleanupFastThreadLocals
boolean
run()
方法完成后自动清理 FastThreadLocal
变量Runnable
决定fallbackThreads
AtomicReference
FastThreadLocalThread
但希望获得类似性能优化的线程 ID (如虚拟线程)java
// 基本构造函数
public FastThreadLocalThread() {
cleanupFastThreadLocals = false; // 不自动清理
}
// 带 Runnable 的构造函数
public FastThreadLocalThread(Runnable target) {
super(FastThreadLocalRunnable.wrap(target)); // 包装 Runnable
cleanupFastThreadLocals = true; // 会自动清理
}
// 其他构造函数类似...
关键点:
Runnable
参数的构造函数都会用 FastThreadLocalRunnable.wrap()
进行包装Runnable
会在执行完成后自动清理 FastThreadLocal
变量threadLocalMap()
和 setThreadLocalMap()
java
public final InternalThreadLocalMap threadLocalMap() {
// 安全检查:只有当前线程才能访问自己的 threadLocalMap
if (this != Thread.currentThread() && logger.isWarnEnabled()) {
logger.warn(new RuntimeException("It's not thread-safe..."));
}
return threadLocalMap;
}
public final void setThreadLocalMap(InternalThreadLocalMap threadLocalMap) {
// 同样的安全检查
if (this != Thread.currentThread() && logger.isWarnEnabled()) {
logger.warn(new RuntimeException("It's not thread-safe..."));
}
this.threadLocalMap = threadLocalMap;
}
java
// 判断当前线程是否会清理 FastThreadLocal
public static boolean currentThreadWillCleanupFastThreadLocals() {
Thread currentThread = currentThread();
if (currentThread instanceof FastThreadLocalThread) {
return ((FastThreadLocalThread) currentThread).willCleanupFastThreadLocals();
}
return isFastThreadLocalVirtualThread(); // 检查是否是虚拟线程
}
// 判断线程是否支持 FastThreadLocal
public static boolean currentThreadHasFastThreadLocal() {
return currentThread() instanceof FastThreadLocalThread ||
isFastThreadLocalVirtualThread();
}
private static boolean isFastThreadLocalVirtualThread() {
long[] arr = fallbackThreads.get();
if (arr == null) {
return false;
}
return Arrays.binarySearch(arr, Thread.currentThread().getId()) >= 0;
}
runWithFastThreadLocal()
(重要!)java
public static void runWithFastThreadLocal(Runnable runnable) {
Thread current = currentThread();
if (current instanceof FastThreadLocalThread) {
throw new IllegalStateException("Caller is a real FastThreadLocalThread");
}
// 1. 将当前线程ID添加到 fallbackThreads
long id = current.getId();
fallbackThreads.updateAndGet(arr -> {
if (arr == null) return new long[]{id};
// 检查是否已存在 (避免重入)
int index = Arrays.binarySearch(arr, id);
if (index >= 0) throw new IllegalStateException("Reentrant call to run()");
// 插入到正确位置 (保持数组有序)
index = ~index;
long[] next = new long[arr.length + 1];
System.arraycopy(arr, 0, next, 0, index);
next[index] = id;
System.arraycopy(arr, index, next, index + 1, arr.length - index);
return next;
});
try {
runnable.run();
} finally {
// 2. 从 fallbackThreads 中移除当前线程ID
fallbackThreads.getAndUpdate(arr -> {
if (arr == null || (arr.length == 1 && arr[0] == id)) {
return null;
}
int index = Arrays.binarySearch(arr, id);
if (index < 0) return arr;
long[] next = new long[arr.length - 1];
System.arraycopy(arr, 0, next, 0, index);
System.arraycopy(arr, index + 1, next, index, arr.length - index - 1);
return next;
});
// 3. 清理所有 FastThreadLocal 变量
FastThreadLocal.removeAll();
}
}
这个方法允许普通线程(包括虚拟线程)临时获得 FastThreadLocal
支持,执行完成后会自动清理。
permitBlockingCalls()
java
public boolean permitBlockingCalls() {
return false; // 默认禁止阻塞调用
}
这是一个钩子方法,子类可以覆盖。Netty 的事件循环线程会利用这个方法检查是否允许阻塞调用。
性能优化:
Thread
并添加 InternalThreadLocalMap
字段,避免了 ThreadLocal
的哈希表查找InternalThreadLocalMap
)资源管理:
FastThreadLocalRunnable
包装确保资源清理runWithFastThreadLocal()
支持非 FastThreadLocalThread
线程线程安全:
threadLocalMap
fallbackThreads
使用 AtomicReference
保证线程安全扩展性:
permitBlockingCalls()
方法允许子类自定义行为FastThreadLocal
:
InternalThreadLocalMap
存储变量FastThreadLocalThread
提供快速访问路径InternalThreadLocalMap
:
FastThreadLocalThread
的成员存储所有线程本地变量FastThreadLocalRunnable
:
Runnable
确保 FastThreadLocal
清理java
final class FastThreadLocalRunnable implements Runnable {
private final Runnable runnable;
public void run() {
try {
runnable.run();
} finally {
FastThreadLocal.removeAll(); // 确保清理
}
}
static Runnable wrap(Runnable runnable) {
return runnable instanceof FastThreadLocalRunnable ? runnable
: new FastThreadLocalRunnable(runnable); // 已包装则直接返回
}
}
这个类的设计充分体现了 Netty 对性能的极致追求,同时保持了良好的资源管理和线程安全性。
为什么Recycler
使用 FastThreadLocal
在 Netty 的 Recycler
中使用 FastThreadLocal
而不是普通 ThreadLocal
主要基于以下几个关键设计考量:
1. 性能优化
FastThreadLocal
相比标准 ThreadLocal
有以下性能优势:
FastThreadLocal
使用固定索引直接访问数组元素,而 ThreadLocal
需要哈希计算和可能的哈希冲突处理ThreadLocal
使用 ThreadLocalMap
(基于哈希表),而 FastThreadLocal
直接使用 InternalThreadLocalMap
的数组结构在 Recycler
高频对象回收场景下,这种优化能显著减少线程本地存储的访问开销。
2. 内存效率
FastThreadLocal
的数组存储方式比 ThreadLocalMap
的 Entry
对象更节省内存InternalThreadLocalMap
会预分配固定大小的数组,避免动态扩容带来的性能波动对于需要维护大量回收对象的 Recycler
来说,内存效率至关重要。
3. 与 Netty 线程模型的深度集成
FastThreadLocalThread
优化:当线程是 FastThreadLocalThread
类型时,InternalThreadLocalMap
会直接存储在线程对象中,完全避免了 ThreadLocal.get()
的查找开销fallbackThreads
机制,Recycler
也能在虚拟线程中保持较好的性能Netty 的默认线程工厂 (DefaultThreadFactory
) 创建的线程都是 FastThreadLocalThread
,这使得 Recycler
能获得最佳性能。
4. 资源清理的确定性
FastThreadLocal
提供了更可靠的资源清理机制:
java
private final FastThreadLocal> threadLocal = new FastThreadLocal>() {
@Override
protected void onRemoval(LocalPool value) throws Exception {
super.onRemoval(value);
// 确保线程终止时清理所有资源
value.pooledHandles = null;
value.owner = null;
handles.clear();
}
};
相比 ThreadLocal
,FastThreadLocal
能更可靠地触发清理回调(通过 FastThreadLocalRunnable
包装)。
5. 与 Recycler
设计的一致性
Recycler
的核心设计理念是避免任何形式的同步开销,这与 FastThreadLocal
的设计目标完全吻合:
get()
) 和回收 (recycle()
) 都依赖线程本地操作LocalPool
的批量处理机制 (chunkSize
) 需要高效访问线程本地队列ThreadLocal
?get()
都需要计算哈希码,在对象池高频访问场景下影响显著ThreadLocalMap.Entry
是对象节点,比数组存储占用更多内存ThreadLocal
依赖 Thread.exit()
清理资源,而 Netty 需要更确定的清理时机FastThreadLocalThread
优化:会失去直接访问线程字段的性能优势在 Recycler.get()
方法中:
public final T get() {
if (maxCapacityPerThread == 0 ||
(PlatformDependent.isVirtualThread(Thread.currentThread()) &&
!FastThreadLocalThread.currentThreadHasFastThreadLocal())) {
return newObject((Handle) NOOP_HANDLE); // 回退路径
}
LocalPool localPool = threadLocal.get(); // 快速路径
// ...
}
当线程是 FastThreadLocalThread
时,threadLocal.get()
实际上是:
InternalThreadLocalMap
index
直接数组访问而标准 ThreadLocal
需要:
ThreadLocalMap
Recycler
使用 FastThreadLocal
的核心原因是其与 Netty 高性能架构的深度契合:
这种设计使得 Netty 的对象回收机制能在高并发场景下保持极高的性能,同时避免传统 ThreadLocal
的内存泄漏风险。