ThreadLocal


一、什么是 ThreadLocal

ThreadLocal 是 Java 提供的线程本地变量(Thread Local Variable)机制:

作用:给每个线程 分配一份独立的变量副本,互不干扰,避免多线程环境下的共享冲突问题。
特点:同一个 ThreadLocal 变量,在不同线程中有不同的值(隔离性)。

本质:每个线程内部有个 ThreadLocalMapThreadLocal 作为 key,自己存自己的值。


二、核心使用示例

public class ThreadLocalDemo {
    private static ThreadLocal threadLocal = ThreadLocal.withInitial(() -> 0);

    public static void main(String[] args) {
        Runnable task = () -> {
            threadLocal.set((int) (Math.random() * 100));
            System.out.println(Thread.currentThread().getName() + ":" + threadLocal.get());
        };
        new Thread(task, "线程A").start();
        new Thread(task, "线程B").start();
    }
}

✅ 输出示例:

线程A:83
线程B:24

各线程独立,互不干扰。


三、ThreadLocal 原理(重点)

1️⃣ 每个线程内部有个 ThreadLocalMap

ThreadLocal.ThreadLocalMap threadLocals;
  • 存储当前线程的所有 ThreadLocal 变量

  • ThreadLocal 对象作为 key

  • 真正的数据存在 ThreadLocalMap.Entry

2️⃣ 存取过程(简化版)

// 存
threadLocal.set("abc"); 
// 本质:Thread.currentThread().threadLocals.put(threadLocal, "abc");

// 取
threadLocal.get();
// 本质:Thread.currentThread().threadLocals.get(threadLocal);

3️⃣ 为什么能做到线程隔离?

因为数据存储在当前线程的 ThreadLocalMap 中,每个线程有自己的副本,自然互不干扰。


四、常见应用场景(面试必问)

场景 说明
✅ 数据库连接 Connection 每个线程绑定一个 Connection,避免频繁创建销毁
✅ 事务管理 线程内管理事务、Session
✅ 用户登录信息存储 比如保存用户 userId,在整个请求链路中随时取
✅ SimpleDateFormat 线程不安全,配合 ThreadLocal 解决并发问题
✅ 防止线程间共享污染 保证每个线程数据独立

五、内存泄漏风险(高频考点)

为什么 ThreadLocal 会引发内存泄漏?

  • ThreadLocalMapkey 是弱引用(WeakReference)value 是强引用

  • 如果 ThreadLocal 被 GC 回收,但 ThreadLocalMap 里的 value 没清除,value 永远被线程引用

  • 特别是 线程池中的线程长时间不销毁,容易发生泄漏

解决方案(开发规范):

✅ 用完一定调用 remove(),及时清理:

try {
    threadLocal.set("xxx");
    // 业务处理
} finally {
    threadLocal.remove();  // 防止内存泄漏
}

六、InheritableThreadLocal(子线程继承)

  • 场景:ThreadLocal 数据传递到子线程中

InheritableThreadLocal local = new InheritableThreadLocal<>();
local.set("main");

new Thread(() -> {
    System.out.println(local.get());  // 输出 "main"
}).start();

✅ 适合做上下文传递,如 分布式链路追踪 TraceId


七、ThreadLocal 方法总结

方法 作用
set(T value) 设置当前线程的局部变量值
get() 获取当前线程的局部变量值
remove() 删除当前线程的局部变量,防止内存泄漏
withInitial(Supplier supplier) 初始化默认值(JDK 8+)

八、面试总结关键点

面试问题 高分回答要点
为什么要用 ThreadLocal? 解决多线程共享变量冲突,做到线程内独享,隔离性强。
底层原理? 每个线程维护一个 ThreadLocalMapThreadLocal 作为 key,存储线程私有数据。
内存泄漏风险? key 弱引用,value 强引用,及时 remove() 防止泄漏,尤其线程池场景。
和 synchronized、锁区别? ThreadLocal 不是用来解决线程安全的共享问题,而是隔离数据,互不共享,避免加锁。

✅ 九、总结一句话

ThreadLocal 本质是线程内的局部存储工具,适合解决“只想让线程自己玩,不共享”的场景,但必须注意 remove(),否则容易内存泄漏。

你可能感兴趣的:(java,jvm,开发语言,多线程)