ThreadLocal的作用和底层原理

作用:

ThreadLocal使得每个线程都可以创建自己共享变量的副本,避免了多线程竞争共享变量

底层原理

当创建ThreadLocal变量时,每个访问该变量的线程都会拥有一个独立的副本,线程通过get( )方法会读取自己线程的副本,set( )时向自己线程的副本写数据。从而解决了线程安全的问题。

复制代码
public class ThreadLocalExample {
    private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);

    public static void main(String[] args) {
        Runnable task = () -> {
            int value = threadLocal.get();
            value += 1;
            threadLocal.set(value);
            System.out.println(Thread.currentThread().getName() + " Value: " + threadLocal.get());
        };

        Thread thread1 = new Thread(task, "Thread-1");
        Thread thread2 = new Thread(task, "Thread-2");

        thread1.start(); // 输出: Thread-1 Value: 1
        thread2.start(); // 输出: Thread-2 Value: 1
    }
}

Thread、ThreadLocal和ThreadLocalMap的关系

答:一个程序只有可以创建多了ThreadLocal对象,每个Thread实例可以通过ThreadLocal对象的set方法向自己的ThreadLocalMap中记录对应的值

每个Thread类中有ThreadLocalMap成员变量。

ThreadLocal在程序中创建,相当于一个存储物体的标签

举个例子:

因为一个程序可以创建多个 ThreadLocal****对象,这就意味着在实际开发中,你可能会有:

  • ThreadLocal<User> userLocal (标签1:存当前登录用户信息)
  • ThreadLocal<Connection> dbLocal (标签2:存数据库连接)
  • ThreadLocal<String> traceIdLocal (标签3:存日志追踪 ID)

当"线程A"在执行任务时,它通过这三个 ThreadLocal 分别调用了 set() 方法。此时,线程A的 ThreadLocalMap**,就整整齐齐地放了三个键值对**。

  • Key是 userLocal,Value 是用户信息;可以通过userLocal.get()方法来获取当前线程存储的登录用户信息
  • Key是 dbLocal,Value 是数据库连接;

可能存在内存泄漏的问题

ThreadLocalMap中key存储的是ThreadLocal变量,value是当前线程要存储的值。key是弱引用、value是强引用。

如果ThreadLocal变量不再被强引用指向时,垃圾回收器会在下次回收时GC该实例。

结果:ThreadLocalMap中的键值对key变为null,value还是存储真实的值。

解决:在使用完ThreadLocal对象后,务必确保该对象被释放。使用 try-finally 块可以确保即使发生异常,remove() 方法也一定会被执行

复制代码
// 定义为 static final,避免重复创建 ThreadLocal 实例
private static final ThreadLocal<UserContext> userContextHolder = new ThreadLocal<>();

public void processRequest(HttpServletRequest request) {
    try {
        // 在 try 块中设置值
        UserContext context = buildUserContext(request);
        userContextHolder.set(context);

        // 执行业务逻辑
        doBusinessLogic();
    } finally {
        // 在 finally 块中必须清理,确保无论是否发生异常都会执行
        userContextHolder.remove();
    }
}
相关推荐
guodingdingh1 小时前
软件开发工作问题总结0718
java·开发语言·数据库
咖啡八杯3 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式
优橙教育3 小时前
5G网优培训 vs Java开发:转行选哪个?
java·开发语言·5g
糖果店的幽灵3 小时前
【DeepAgents 从入门到精通】Context Management 上下文管理
java·人工智能·后端·spring·中间件·langgraph·deepagents
腻害兔4 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:字典、短信、邮件、通知——后台系统的“基础设施四件套“!
java·前端·vue.js·产品经理·ai编程
SeaTunnel5 小时前
从 Python Script 地狱到标准化数据集成框架
大数据·开发语言·python·程序员·代码·seatunnel
碎光拾影5 小时前
ARM交叉工具链各工具作用及IMX6ULL平台LED+蜂鸣器裸机程序实现
java·开发语言·数据库
Marst Code6 小时前
(python)2026Plotly 库评估:交互式可视化到底值不值得引入?
开发语言·python
Miao121316 小时前
微服务 API 测试实践:海外某民宿平台如何构建模式驱动测试基础设施
java·开发语言
腻害兔6 小时前
【若依项目-产品经理视角】RuoYi-Vue-Pro 源码拆解:支付模块 yudao-module-pay,一个让产品经理都看懂的支付中台设计
java·前端·vue.js·产品经理·ai编程