ThreadLocal
一、是什么?
ThreadLocal 是 Java 中的一个工具类(java.lang.ThreadLocal
),用于创建线程局部变量。每个线程通过 ThreadLocal 可以拥有该变量的独立副本,线程之间互不干扰,实现线程隔离的数据存储。
二、解决什么问题
- 线程安全问题:解决多线程环境下共享变量的并发冲突(如
SimpleDateFormat
非线程安全)。 - 参数传递冗余:避免在方法调用链中显式传递参数(如用户身份信息)。
- 资源隔离:为每个线程提供独立资源(如数据库连接),避免重复创建。
三、核心方法
方法 | 说明 |
---|---|
T get() | 返回当前线程的变量副本值 |
void set(T value) | 设置当前线程的变量副本值 |
void remove() | 移除当前线程的变量副本(防止内存泄漏) |
static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) | (JDK8+)静态工厂方法,通过 Supplier 初始化变量 |
四、应用场景
- 线程不安全工具类java
private static final ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd")); // 每个线程独立使用 SimpleDateFormat
- Web 请求上下文(如 Spring 的
RequestContextHolder
) - 数据库连接管理(如为每个线程分配独立
Connection
) - 用户会话信息(避免在方法参数中传递用户 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 (线程间独立)
}
}
六、重要注意事项
- 内存泄漏风险
- ThreadLocal 的 Entry 使用弱引用(Key),但 Value 是强引用。
- 线程池中线程长期存活时,若不调用
remove()
,Value 会持续占用内存。 - 解决:始终在
try-finally
中调用remove()
:javatry { threadLocal.set(data); // ...业务逻辑 } finally { threadLocal.remove(); // 强制清理 }
- 继承性问题
- 子线程默认无法访问父线程的 ThreadLocal 变量。
- 需使用
InheritableThreadLocal
(但线程池场景仍可能失效)。
- 性能影响
- 频繁读写时,ThreadLocal 的哈希表性能低于局部变量。
七、总结
ThreadLocal 通过线程隔离机制解决共享资源并发问题,适用于线程不安全工具类、上下文传递等场景。JDK8 的 withInitial()
简化了初始化,但必须警惕内存泄漏,务必配合 remove()
清理数据。它是轻量级线程封闭的首选方案,但非分布式场景(跨 JVM)需改用 Redis 等方案。