ThreadLocal 的 get() 方法通过哈希算法 + 线性探测法定位 ThreadLocalMap 中 key 的具体位置,具体流程如下:
一、定位 key 的核心步骤
-
计算初始索引
- 根据当前
ThreadLocal对象的threadLocalHashCode(唯一哈希值),计算初始槽位:int i = key.threadLocalHashCode & (table.length - 1)。
- 根据当前
-
线性探测查找匹配项
- 若当前槽位的 key 不匹配,则调用
nextIndex()向后线性探测(即i = (i + 1) % table.length)。 - 若遇到 key 匹配的槽位,直接返回对应 value。
- 若探测到
Entry为null的槽位,说明 key 不存在,触发初始化逻辑(调用initialValue())。
- 若当前槽位的 key 不匹配,则调用
-
清理过期 Entry
- 探测过程中如发现 key 已被回收的弱引用(即
Entry.get() == null),触发expungeStaleEntry()清理无效 Entry,避免内存泄漏。
- 探测过程中如发现 key 已被回收的弱引用(即
二、源码逻辑示例(简化)
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); // 获取当前线程的 ThreadLocalMap
if (map != null) {
// 通过哈希值计算初始索引
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
// 线性探测直到找到匹配项或空槽
while (e != null) {
if (e.get() == key) return (T) e.value; // 匹配成功
i = nextIndex(i, len); // 继续探测
e = table[i];
}
}
return setInitialValue(); // 初始化值(内部调用 initialValue())
}
三、与 set() 方法的关联性
| 操作 | 哈希冲突解决逻辑 | 清理机制触发条件 |
|---|---|---|
set() |
通过开放地址法找到下一个可用槽位并插入或替换值 |
插入时触发探测清理(惰性清理) |
get() |
通过相同哈希算法反向探测匹配的 key |
探测过程中发现过期 Entry 时清理 |
四、设计要点
-
哈希一致性
set()和get()使用相同的哈希算法和探测逻辑,确保数据存取路径一致。 -
内存泄漏防御
通过弱引用 key 和探测式清理,减少因未调用remove()导致的内存泄漏风险。 -
时间复杂度
最坏情况下需遍历整个数组,但实际因惰性清理和扩容机制,平均复杂度接近 O(1)。
通过上述机制,ThreadLocal 的 get() 方法在开放地址法基础上,高效定位 key 并保证线程安全的数据隔离性
浙公网安备 33010602011771号