ThreadLocal
是 Java 提供的一种线程隔离机制 ,用于在每个线程中独立存储变量副本,避免多线程环境下变量共享导致的线程安全问题。
🧠 作用
ThreadLocal
的核心作用是:每个线程拥有自己独立的变量副本,互不影响,从而实现线程间的数据隔离。
📌 常用方法
方法 | 描述 |
---|---|
void set(T value) |
设置当前线程的线程局部变量副本 |
T get() |
获取当前线程的线程局部变量副本 |
void remove() |
移除当前线程的线程局部变量副本 |
protected T initialValue() |
初始化默认值(可重写) |
🧪 使用示例
示例 1:线程独立计数器
java
ThreadLocal<Integer> counter = ThreadLocal.withInitial(() -> 0);
Runnable task = () -> {
for (int i = 0; i < 5; i++) {
int val = counter.get() + 1;
counter.set(val);
System.out.println(Thread.currentThread().getName() + ": " + val);
}
};
new Thread(task).start();
new Thread(task).start();
输出:
makefile
Thread-0: 1
Thread-0: 2
Thread-0: 3
Thread-1: 1
Thread-1: 2
Thread-1: 3
每个线程的计数器是独立的。
示例 2:上下文传递(如用户信息、事务ID等)
java
public class UserContext {
private static final ThreadLocal<String> currentUser = new ThreadLocal<>();
public static void setCurrentUser(String user) {
currentUser.set(user);
}
public static String getCurrentUser() {
return currentUser.get();
}
public static void clear() {
currentUser.remove();
}
}
// 使用
UserContext.setCurrentUser("user1");
System.out.println(UserContext.getCurrentUser()); // 输出 user1
✅ 使用场景
场景 | 描述 |
---|---|
用户上下文 | 存储当前线程的用户信息(如登录ID、权限等) |
事务管理 | 在事务处理中保存当前事务对象 |
日志追踪 | 记录请求的唯一标识(traceId) |
数据库连接 | 每个线程使用自己的数据库连接(避免并发问题) |
⚠️ 注意事项
1. 内存泄漏风险
- 如果使用
ThreadLocal
后没有调用remove()
,可能导致内存泄漏。 - 在使用线程池时,线程会被复用,务必在使用完后清理变量。
java
try {
userLocal.set(user);
// ... 业务逻辑
} finally {
userLocal.remove(); // 必须清理
}
2. 不适用于共享状态
ThreadLocal
是线程隔离的,不能用于线程间共享数据。
💡 在你当前项目中的使用建议
在你的项目中,ThreadLocal
可用于以下场景:
✅ 场景 1:用户登录上下文隔离
java
public class LoginContext {
private static final ThreadLocal<String> currentUserCode = new ThreadLocal<>();
public static void setLoginCode(String code) {
currentUserCode.set(code);
}
public static String getLoginCode() {
return currentUserCode.get();
}
public static void clear() {
currentUserCode.remove();
}
}
在拦截器中设置用户信息:
java
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("Authorization");
String userCode = parseToken(token);
LoginContext.setLoginCode(userCode);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
LoginContext.clear(); // 必须清除,防止内存泄漏
}
✅ 场景 2:事务上下文隔离
java
public class TransactionContext {
private static final ThreadLocal<String> transactionId = new ThreadLocal<>();
public static void setTransactionId(String id) {
transactionId.set(id);
}
public static String getTransactionId() {
return transactionId.get();
}
public static void clear() {
transactionId.remove();
}
}
📚 总结
特性 | 说明 |
---|---|
线程隔离 | 是 |
是否线程安全 | 是(每个线程独立) |
是否需要清理 | 是(避免内存泄漏) |
适用场景 | 用户上下文、日志追踪、事务管理等 |
🧩 与 InheritableThreadLocal
的区别
特性 | ThreadLocal |
InheritableThreadLocal |
---|---|---|
是否传递给子线程 | 否 | 是 |
适用场景 | 线程内部隔离 | 父子线程间传递数据 |
java
InheritableThreadLocal<String> inheritable = new InheritableThreadLocal<>();
inheritable.set("parent");
new Thread(() -> {
System.out.println(inheritable.get()); // 输出 parent
}).start();
如你有更具体的使用场景或问题,可以进一步提供代码片段,我可以帮你具体分析 ThreadLocal
的使用方式和优化建议。