ThreadLocal 是 Java 中一个非常重要但容易被误解的工具,尤其是在 多线程开发 中。它和普通变量、线程共享变量有本质区别,适用于一些每个线程都需要独立数据副本的场景。
✅ 一、什么是 ThreadLocal?
ThreadLocal 代表线程局部变量:每个线程有自己独立的副本变量,互不干扰。
简单说: “变量是你自己的,别人线程拿不到” 。
📦 二、ThreadLocal 的原理结构
每个线程(Thread)内部都有一个 ThreadLocalMap,用于存储所有该线程自己的 ThreadLocal 变量。
ThreadLocal<String> local = new ThreadLocal<>();
local.set("小杰"); // 只对当前线程可见
背后大致等价于:
threadLocalMap.put(currentThread, "小杰");
所以:
- 每个线程都有一份自己的变量副本;
- 不同线程对同一个
ThreadLocal实例,存的是自己线程内部的一份值。
🧪 三、使用示例
示例1:基本用法
public class ThreadLocalExample {
private static final ThreadLocal<String> userHolder = new ThreadLocal<>();
public static void main(String[] args) {
Runnable task = () -> {
userHolder.set(Thread.currentThread().getName());
System.out.println("线程:" + Thread.currentThread().getName() + " 的用户是:" + userHolder.get());
};
new Thread(task, "线程A").start();
new Thread(task, "线程B").start();
}
}
📌 输出结果中每个线程都能拿到自己的值,不冲突。
示例2:配合数据库连接、登录用户、事务等
public class UserContext {
private static final ThreadLocal<String> currentUser = new ThreadLocal<>();
public static void setUser(String username) {
currentUser.set(username);
}
public static String getUser() {
return currentUser.get();
}
public static void clear() {
currentUser.remove(); // ✅ 必须手动清除,防止内存泄露
}
}
用法:
UserContext.setUser("小杰");
System.out.println("当前用户:" + UserContext.getUser());
UserContext.clear();
🚨 四、注意事项(重要)
| ⚠️ 问题 | 描述 |
|---|---|
| 内存泄漏风险 | 如果是在线程池中使用 ThreadLocal,线程不会销毁,不手动 .remove() 会导致对象一直存在内存中 |
| 生命周期长 | 一定记得在使用完后调用 .remove() |
| 线程隔离,不共享 | 如果你想在线程之间共享变量,不适用 ThreadLocal,应用共享变量、锁等 |
📌 五、典型应用场景
| 场景 | 说明 |
|---|---|
| 登录用户信息 | 每个请求线程记录当前登录用户 |
| 日志链路追踪 | 每个请求线程绑定唯一 traceId |
| 数据源切换 | 多数据源情况下记录当前线程使用哪个 DB |
| 数据库连接管理 | 每个线程持有独立连接(如 Spring JDBC) |
✅ 总结一句话
ThreadLocal 是实现 线程内数据隔离 的工具,适合在高并发环境下存储用户上下文、连接等数据,但必须及时清除,否则容易内存泄漏。