你好,我是风一样的树懒,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。
文章可能会比较长,主要解析的非常详解,或涉及一些底层知识,供面试高阶难度用。可以根据自己实际理解情况合理取舍阅读
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()); // 输出 12345RequestContext.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);}}
优势
每个线程独立管理自己的对象池,提高性能。
今天的内容就分享到这儿,喜欢的朋友可以关注,点赞。有什么不足的地方欢迎留言指出,您的关注是我前进的动力!