FastThreadLocal快在用预分配索引直接数组访问,避免哈希计算与线性探测;而ThreadLocal依赖ThreadLocalMap哈希表,存在冲突、清理和缓存不友好等性能瓶颈。

FastThreadLocal 的核心优化在于把“查表”变成“直接取数”——它不用哈希计算和冲突探测,而是用一个预分配的整数索引,直接访问数组中的指定位置。
为什么普通 ThreadLocal 慢?
Java 原生 ThreadLocal 每个线程都持有一个 ThreadLocalMap,本质是基于开放地址法的哈希表。每次 get() 或 set() 都要:
- 计算 ThreadLocal 实例的 hash 值
- 根据 hash 找到初始槽位
- 若发生冲突,逐个探测下一个位置(线性探测)
- 还要检查 key 是否为 null,做脏 entry 清理
即使只存几个变量,这套流程也必须完整执行,CPU 缓存不友好,最坏情况时间复杂度接近 O(n)。
FastThreadLocal 怎么做到快?
它绕开了哈希逻辑,靠三步实现常量级访问:
- 每个 FastThreadLocal 实例在创建时,通过 AtomicInteger 获取一个全局唯一、递增的 int 索引(如 0、1、2…)
- 线程对象(必须是 FastThreadLocalThread)内部持有 InternalThreadLocalMap,其核心字段是一个 Object[] 数组(indexedVariables)
- get() 就是 array[index],set() 就是 array[index] = value —— 纯内存偏移访问,无分支、无循环、无 hash 计算
这个数组默认初始容量 32,按需扩容,旧数据自动拷贝,不无限增长。
关键前提:线程类型必须匹配
FastThreadLocal 不是“拿来即用”的替代品,它依赖底层支持:
- 只有 FastThreadLocalThread 实例才自带 InternalThreadLocalMap 字段;普通 Thread 会退化为慢路径(fallback 到标准 ThreadLocal 行为)
- Netty 的 EventLoop 线程天然是 FastThreadLocalThread;你自己新建线程时,得显式使用 new FastThreadLocalThread(runnable)
- Spring 的 ThreadPoolTaskExecutor 默认用 Thread,需自定义 ThreadFactory 返回 FastThreadLocalThread
性能差异的真实体现
在高并发、高频次访问场景下(比如每秒百万次上下文切换),数组下标访问比哈希表查找节省数十纳秒/次。累积起来,可降低整体延迟 10%–30%,尤其在 CPU 密集型 IO 处理中效果显著。这不是微观优化,而是 Netty 支撑百万连接的关键细节之一。


















