你好,我是风一样的树懒,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。
文章可能会比较长,主要解析的非常详解,或涉及一些底层知识,供面试高阶难度用。可以根据自己实际理解情况合理取舍阅读
ThreadLocal 主要用于在 多线程环境下,每个线程都需要有自己独立的数据副本,避免线程间共享变量导致的并发问题,而不需要使用 synchronized 进行同步。
适用于 同一线程内变量共享,但 线程之间相互隔离 的场景。
在 多线程环境 下,如果一个变量需要在 同一线程的多个方法或类中共享,但不希望被其他线程访问。
例如,每个用户请求都会创建一个 requestId,在当前线程中传递,但不希望不同线程共享这个 requestId。
public class RequestContext {
private static final ThreadLocal<String> requestId = new ThreadLocal<>();
public static void setRequestId(String id) {
requestId.set(id);
}
public static String getRequestId() {
return requestId.get();
}
public static void removeRequestId() {
requestId.remove();
}
}
RequestContext.setRequestId("12345");
System.out.println(RequestContext.getRequestId()); // 输出 12345
RequestContext.removeRequestId();
不同线程的 requestId 互不干扰。
保证同一事务(线程)内使用同一个数据库连接,防止 多次创建和关闭连接,提高性能。
Spring 事务管理(DataSourceTransactionManager) 就是基于 ThreadLocal 存储 数据库连接对象(Connection)。
public class ConnectionManager {
private static final ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();
public static Connection getConnection() {
return connectionHolder.get();
}
public static void setConnection(Connection conn) {
connectionHolder.set(conn);
}
public static void removeConnection() {
connectionHolder.remove();
}
}
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "user", "password");
ConnectionManager.setConnection(conn);
Connection c = ConnectionManager.getConnection(); // 获取的是同一个连接
ConnectionManager.removeConnection(); // 释放资源
优势
避免在同一事务中创建多个连接,提高性能。
保证数据库事务的一致性。
在 Web 请求 处理中,用户身份信息 需要在 整个请求生命周期内传递,但 不希望被其他线程访问。
ThreadLocal 可以让 同一个线程内的所有代码访问当前用户,避免使用 HttpSession 共享数据。
public class UserContext {
private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<>();
public static void setUser(User user) {
userThreadLocal.set(user);
}
public static User getUser() {
return userThreadLocal.get();
}
public static void removeUser() {
userThreadLocal.remove();
}
}
// 用户登录时
User user = new User("Alice");
UserContext.setUser(user);
// 在 Controller 或 Service 层获取用户
User currentUser = UserContext.getUser();
System.out.println(currentUser.getName()); // 输出 Alice
// 请求结束时清理
UserContext.removeUser();
优势
避免显式传递 User 对象,简化代码。
保证用户信息线程安全,不同线程不会访问到其他用户的数据。
分布式系统 需要 日志追踪(TraceId),保证 同一个请求的所有日志都有相同的 TraceId。
ThreadLocal 让 相同请求(线程)内的所有方法共享 TraceId。
public class LogTraceContext {
private static final ThreadLocal<String> traceIdHolder = new ThreadLocal<>();
public static void setTraceId(String traceId) {
traceIdHolder.set(traceId);
}
public static String getTraceId() {
return traceIdHolder.get();
}
public static void removeTraceId() {
traceIdHolder.remove();
}
}
LogTraceContext.setTraceId(UUID.randomUUID().toString());
System.out.println("TraceId: " + LogTraceContext.getTraceId()); // 记录日志
LogTraceContext.removeTraceId();
优势
保证日志的可追溯性,便于排查问题。
不同线程的 TraceId 互不影响。
SimpleDateFormat 不是线程安全的,但 每个线程都需要一个 SimpleDateFormat 实例。
ThreadLocal 解决 日期格式化的线程安全问题。
public class DateFormatter {
private static final ThreadLocal<SimpleDateFormat> dateFormatHolder =
ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
public static String format(Date date) {
return dateFormatHolder.get().format(date);
}
}
String dateStr = DateFormatter.format(new Date());
System.out.println(dateStr);
优势
每个线程都有自己的 SimpleDateFormat 实例,避免线程安全问题。
提高性能,避免每次创建 SimpleDateFormat 对象。
ThreadLocal 不能被子线程继承,但 InheritableThreadLocal 可以。
适用于 父线程创建子线程时,子线程需要继承父线程的数据。
public class InheritableContext {
private static final InheritableThreadLocal<String> inheritableThreadLocal =
new InheritableThreadLocal<>();
public static void set(String value) {
inheritableThreadLocal.set(value);
}
public static String get() {
return inheritableThreadLocal.get();
}
public static void remove() {
inheritableThreadLocal.remove();
}
}
InheritableContext.set("Parent Thread Data");
new Thread(() -> {
System.out.println("Child Thread: " + InheritableContext.get());
}).start();
输出
Child Thread: Parent Thread Data
优势
父线程的数据可以自动传递到子线程,适用于任务上下文传递。
在 高并发场景,可以为每个线程创建自己的对象池,减少对象创建开销。
public class ConnectionPool {
private static final ThreadLocal<Queue<Connection>> connectionPool =
ThreadLocal.withInitial(LinkedList::new);
public static Connection getConnection() {
Queue<Connection> pool = connectionPool.get();
return pool.poll();
}
public static void returnConnection(Connection conn) {
connectionPool.get().offer(conn);
}
}
优势
每个线程独立管理自己的对象池,提高性能。
今天的内容就分享到这儿,喜欢的朋友可以关注,点赞。有什么不足的地方欢迎留言指出,您的关注是我前进的动力!