ThreadLocal是Java提供的线程本地变量机制,用于实现线程级别的数据隔离。每个访问该变量的线程都会获得独立的变量副本,适用于需要避免线程间共享数据的场景。
特点:
public class ThreadLocal<T> {
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 每个线程拥有独立的ThreadLocalMap实例
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
return (T)e.value;
}
}
return setInitialValue();
}
}
数据结构:
ThreadLocalMap
场景需求:在Web请求处理链中传递用户身份信息
public class UserContext {
private static ThreadLocal<User> userHolder = new ThreadLocal<>();
public static void setUser(User user) {
userHolder.set(user);
}
public static User getUser() {
return userHolder.get();
}
public static void clear() {
userHolder.remove(); // 必须清理防止内存泄漏
}
}
// 拦截器中设置用户信息
public class AuthInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse res, Object o) {
User user = authService.verify(request.getHeader("token"));
UserContext.setUser(user); // 存入ThreadLocal
return true;
}
@Override
public void afterCompletion(HttpServletRequest req, HttpServletResponse res, Object o, Exception e) {
UserContext.clear(); // 请求结束清理
}
}
// Service层直接获取
@Service
public class OrderService {
public void createOrder() {
User user = UserContext.getUser(); // 无需参数传递
System.out.println("创建订单,用户:" + user.getId());
}
}
场景需求:保证同一事务中使用的数据库连接一致
public class ConnectionManager {
private static ThreadLocal<Connection> connHolder = ThreadLocal.withInitial(() -> {
try {
return DriverManager.getConnection("jdbc:mysql://localhost:3306/test");
} catch (SQLException e) {
throw new RuntimeException("获取连接失败", e);
}
});
public static Connection getConn() {
return connHolder.get();
}
public static void close() {
Connection conn = connHolder.get();
if (conn != null) {
try {
conn.close();
} catch (SQLException ignored) {}
connHolder.remove(); // 关键!
}
}
}
// 使用示例
public void executeQuery() {
try {
Connection conn = ConnectionManager.getConn();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM user");
// 处理结果...
} finally {
ConnectionManager.close(); // 确保关闭并清理
}
}
场景需求:SimpleDateFormat非线程安全,同步使用性能低。
public class DateUtils {
private static ThreadLocal<SimpleDateFormat> sdfHolder = ThreadLocal.withInitial(
() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
);
public static String format(Date date) {
return sdfHolder.get().format(date);
}
}
// 多线程并发调用安全
ExecutorService pool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
pool.execute(() -> {
String dateStr = DateUtils.format(new Date());
System.out.println(dateStr);
});
}
场景需求:在多层方法调用中传递事务状态
public class TransactionContext {
private static ThreadLocal<Boolean> transactionActive =
ThreadLocal.withInitial(() -> false);
public static void begin() {
transactionActive.set(true);
}
public static boolean isActive() {
return transactionActive.get();
}
public static void end() {
transactionActive.remove();
}
}
// 使用AOP管理事务
@Aspect
public class TransactionAspect {
@Around("@annotation(transactional)")
public Object manageTransaction(ProceedingJoinPoint pjp) throws Throwable {
try {
TransactionContext.begin();
Object result = pjp.proceed();
TransactionContext.end();
return result;
} catch (Exception e) {
TransactionContext.end();
throw e;
}
}
}
需求:为每个请求生成唯一TraceID,贯穿日志打印、RPC调用等环节。
public class TraceContext {
private static ThreadLocal<String> traceIdHolder = new ThreadLocal<>();
public static void startTrace() {
traceIdHolder.set(UUID.randomUUID().toString());
}
public static String getTraceId() {
return traceIdHolder.get();
}
public static void endTrace() {
traceIdHolder.remove();
}
}
// 日志切面增强
@Aspect
@Component
public class LogAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
MDC.put("traceId", TraceContext.getTraceId()); // 日志框架集成
try {
return pjp.proceed();
} finally {
MDC.clear();
TraceContext.endTrace();
}
}
}
remove()
try {
threadLocal.set(value);
// 业务逻辑...
} finally {
threadLocal.remove(); // 必须清理
}
适用场景:
优势:
使用原则:
set-remove
配对原则