ThreadLocal 详解

1. 是什么

ThreadLocal 是线程本地变量,给每个线程提供独立的变量副本,互不影响。

2. 简单用法

java 复制代码
public class UserContext {
    private static final ThreadLocal<String> USER = new ThreadLocal<>();

    public static void set(String user) {
        USER.set(user);
    }

    public static String get() {
        return USER.get();
    }

    public static void remove() {
        USER.remove();
    }
}

// 使用
UserContext.set("张三");
String user = UserContext.get();  // 同一个线程里拿到的都是"张三"
UserContext.remove();

3. 常见场景

用户信息传递

不用每次方法参数传递:

java 复制代码
// 登录时存进去
UserContext.set(SecurityContext.getUser());

// 任何地方都能取到
public void anyMethod() {
    User user = UserContext.get();  // 拿到当前登录用户
}

数据库连接

保证同一线程用的是同一个连接:

java 复制代码
private static final ThreadLocal<Connection> DB = new ThreadLocal<>();

// 获取连接
public Connection getConnection() {
    Connection conn = DB.get();
    if (conn == null) {
        conn = dataSource.getConnection();
        DB.set(conn);
    }
    return conn;
}

请求Tracer

java 复制代码
private static final ThreadLocal<String> TRACE_ID = new ThreadLocal<>();

public void handleRequest() {
    TRACE_ID.set(UUID.randomUUID().toString());
    log.info("开始处理, traceId={}", TRACE_ID.get());
    // 整个调用链都能拿到同一个 traceId
}

4. 原理

每个 Thread 对象里有个 ThreadLocalMap:

复制代码
Thread
  └── threadLocals: ThreadLocalMap
        ├── ThreadLocal<?>  →  value
        ├── ThreadLocal<?>  →  value
        └── ...

set 时,把 ThreadLocal 对象作为 key,存到当前线程的 Map 里。

get 时,从当前线程的 Map 里,用 ThreadLocal 对象作为 key 取值。

所以不同线程访问同一个 ThreadLocal,拿到的值不一样。

5. 内存泄漏

ThreadLocalMap 的 Entry 继承 WeakReference:

java 复制代码
static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

key(ThreadLocal 对象)是弱引用,理论上会被 GC 回收。但 value 是强引用,可能造成泄漏。

所以用完要 remove!

java 复制代码
try {
    User user = UserContext.get();
} finally {
    UserContext.remove();  // 用完清理
}

6. 总结

  • 每个线程独立副本,互不干扰
  • 常用场景:用户信息传递、数据库连接、链路追踪
  • 用完记得 remove,防止内存泄漏
相关推荐
SL_staff1 小时前
打破风控迭代死循环:用决策编排重构业务与技术协作链路
java·产品·全栈
Mikko71 小时前
Redis 连接数满了服务端有错误信息吗?连接数不增加却命令超时?6.2 实测连接池、超时与大 key 怎么判定
java·spring boot·redis·后端
.Bryce.1 小时前
从零到一开发技术名词大全:从需求分析到 AI Agent 工程
java·网络·人工智能·agent·需求分析·agent评测
SL_staff2 小时前
JVS-APS的7个刚性约束设计:从无限产能幻觉到可执行排产的工程实践
java·spring cloud·全栈
akinggw2 小时前
libagentcore - 一个基于c++的AI多agent系统升级到2.0版本
开发语言·jvm·c++
代码山河3 小时前
基本类型转换:自动类型转换与强制类型转换
java·学习·架构·教程·面向对象·项目
lisw053 小时前
互联网上的数据结构可视化!
数据结构·信息可视化
Escalating_xu4 小时前
【Linux 网络编程】五种 I/O 模型与非阻塞 I/O:从“等数据”到正确处理 EAGAIN
java·linux·网络
程序猿乐锅5 小时前
万字详解Single-flight(单飞模式)
java·开发语言·分布式·后端·学习·single-flight
Java后端的Ai之路5 小时前
【AIAgent开发】-Agent工具封装成CLI(纯干货!!!)
java·javascript·人工智能·agent·cli