ThreadLocal原理

ThreadLocal 原理

概述

ThreadLocal 提供线程本地变量,每个线程都有自己独立的变量副本,实现线程间数据隔离。

核心数据结构

1
2
3
4
Thread
└── ThreadLocalMap threadLocals
└── Entry[] table
└── Entry(ThreadLocal<?> key, Object value)
组件 说明
Thread.threadLocals 每个线程持有的 ThreadLocalMap 实例
ThreadLocalMap 自定义哈希表,存储线程本地变量
Entry 继承 WeakReference<ThreadLocal<?>> 的键值对

工作原理

1. set() 方法

1
2
3
4
5
6
7
8
9
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); // 获取当前线程的 ThreadLocalMap
if (map != null) {
map.set(this, value); // this 是 ThreadLocal 实例作为 key
} else {
createMap(t, value); // 首次使用,创建 map
}
}

2. get() 方法

1
2
3
4
5
6
7
8
9
10
11
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
Entry e = map.getEntry(this);
if (e != null) {
return (T) e.value;
}
}
return setInitialValue(); // 返回初始值
}

3. 数据存储位置

graph LR
    A[ThreadLocal.get/set] --> B[Thread.currentThread]
    B --> C[thread.threadLocals]
    C --> D[ThreadLocalMap]
    D --> E[Entry数组]
    E --> F["key=ThreadLocal实例<br/>value=线程本地值"]

关键设计

Entry 使用弱引用

1
2
3
4
5
6
7
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k); // key 是弱引用
value = v; // value 是强引用
}
}

为什么 key 用弱引用? 👉Java引用类型详解 ^weak-ref-design

  • 当外部不再持有 ThreadLocal 引用时,GC 可以回收 ThreadLocal 对象
  • 避免 ThreadLocal 对象本身的内存泄漏

哈希冲突处理

ThreadLocalMap 使用线性探测法解决哈希冲突:

1
2
3
4
5
6
7
8
9
10
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len - 1); // 计算下标

// 线性探测
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
// 找到相同 key 或空槽位
}
}

内存泄漏问题

泄漏场景

条件 结果
ThreadLocal 被回收(弱引用) Entry.key = null
线程仍存活(如线程池) Entry.value 无法回收

解决方案

始终在使用后调用 remove()

1
2
3
4
5
6
7
8
ThreadLocal<User> userContext = new ThreadLocal<>();

try {
userContext.set(currentUser);
// 业务逻辑
} finally {
userContext.remove(); // 必须清理!
}

典型应用场景

场景 示例
用户上下文 存储当前登录用户信息
数据库连接 保证同一线程使用同一连接
事务管理 Spring 的事务同步
日期格式化 SimpleDateFormat 线程安全使用
链路追踪 TraceId 在线程内传递

与同步机制对比

特性 ThreadLocal synchronized/Lock
原理 空间换时间,每个线程一份副本 时间换空间,排队访问
性能 无竞争,性能高 有锁竞争开销
适用场景 线程独享数据 线程共享数据

总结

  1. 存储位置:数据存在每个线程的 threadLocals 字段中
  2. 数据结构:ThreadLocalMap 是自定义哈希表,Entry 的 key 是弱引用
  3. 核心思想:用空间换时间,避免同步开销
  4. 注意事项:使用后必须调用 remove() 防止内存泄漏

ThreadLocal原理
https://zmmmmy.github.io/2026/01/10/ThreadLocal原理/
作者
ZhiMy
发布于
2026年1月10日
许可协议