Skip to content

ThreadLocal

一、是什么?

ThreadLocal 是 Java 中的一个工具类(java.lang.ThreadLocal),用于创建线程局部变量。每个线程通过 ThreadLocal 可以拥有该变量的独立副本,线程之间互不干扰,实现线程隔离的数据存储。

二、解决什么问题

  1. 线程安全问题:解决多线程环境下共享变量的并发冲突(如 SimpleDateFormat 非线程安全)。
  2. 参数传递冗余:避免在方法调用链中显式传递参数(如用户身份信息)。
  3. 资源隔离:为每个线程提供独立资源(如数据库连接),避免重复创建。

三、核心方法

方法说明
T get()返回当前线程的变量副本值
void set(T value)设置当前线程的变量副本值
void remove()移除当前线程的变量副本(防止内存泄漏)
static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier)(JDK8+)静态工厂方法,通过 Supplier 初始化变量

四、应用场景

  1. 线程不安全工具类
    java
    private static final ThreadLocal<SimpleDateFormat> dateFormat = 
        ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
    // 每个线程独立使用 SimpleDateFormat
  2. Web 请求上下文(如 Spring 的 RequestContextHolder
  3. 数据库连接管理(如为每个线程分配独立 Connection
  4. 用户会话信息(避免在方法参数中传递用户 ID)

五、Java 示例

java
public class ThreadLocalDemo {
    private static final ThreadLocal<Integer> threadLocalCounter = 
        ThreadLocal.withInitial(() -> 0); // JDK8+ 初始化方式

    public static void main(String[] args) {
        Runnable task = () -> {
            int count = threadLocalCounter.get();
            threadLocalCounter.set(count + 1);
            System.out.println(Thread.currentThread().getName() + ": " + threadLocalCounter.get());
            threadLocalCounter.remove(); // 必须移除!
        };

        new Thread(task).start(); // 输出 Thread-0: 1
        new Thread(task).start(); // 输出 Thread-1: 1 (线程间独立)
    }
}

六、重要注意事项

  1. 内存泄漏风险
    • ThreadLocal 的 Entry 使用弱引用(Key),但 Value 是强引用。
    • 线程池中线程长期存活时,若不调用 remove(),Value 会持续占用内存。
    • 解决:始终在 try-finally 中调用 remove()
      java
      try {
          threadLocal.set(data);
          // ...业务逻辑
      } finally {
          threadLocal.remove(); // 强制清理
      }
  2. 继承性问题
    • 子线程默认无法访问父线程的 ThreadLocal 变量。
    • 需使用 InheritableThreadLocal(但线程池场景仍可能失效)。
  3. 性能影响
    • 频繁读写时,ThreadLocal 的哈希表性能低于局部变量。

七、总结

ThreadLocal 通过线程隔离机制解决共享资源并发问题,适用于线程不安全工具类、上下文传递等场景。JDK8 的 withInitial() 简化了初始化,但必须警惕内存泄漏,务必配合 remove() 清理数据。它是轻量级线程封闭的首选方案,但非分布式场景(跨 JVM)需改用 Redis 等方案。