ThreadLocal 为每个线程自动维护独立变量副本,通过各线程独有的 ThreadLocalMap 实现隔离;只需调用 set()/get(),无需手动复制;需注意内存泄漏,长期运行线程应显式 remove();可封装对象以逻辑上存储多个变量。

ThreadLocal 本身不“存储多个副本”,而是为每个线程自动维护一个独立的变量副本。你不需要手动存多份,Java 在底层通过线程对象(Thread)持有的 ThreadLocalMap 来实现“每个线程一份”。关键在于:每个线程第一次调用 get() 或 set() 时,会为自己创建专属副本。
ThreadLocal 的存储结构:每个线程自带 Map
每个 Thread 实例内部都有一个 threadLocals 字段(类型是 ThreadLocalMap),它不是静态共享的,而是每个线程独有:
-
ThreadLocalMap是一个定制的哈希表,key 是ThreadLocal实例(弱引用),value 是该线程对应的变量值; - 不同线程操作同一个
ThreadLocal变量时,实际访问的是各自Thread.threadLocals中的不同 bucket; - 所以“多个线程独立副本”是自动发生的,无需显式复制或分发。
怎么正确使用:set() 和 get() 就够了
你只需在各线程中对同一个 ThreadLocal 实例调用 set() 和 get(),JVM 自动隔离:
Java Maven 项目审查工作流的通用输入处理。当 Java Maven 项目以 ZIP压缩包或 GitLab 仓库 URL 形式提供且需要...时使用。
static ThreadLocal<SimpleDateFormat> sdfHolder = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));
- 线程 A 调用
sdfHolder.set(new SimpleDateFormat("HH:mm"))→ 值存入 A 的ThreadLocalMap; - 线程 B 调用
sdfHolder.get()→ 返回 B 自己 map 里初始化的"yyyy-MM-dd"格式实例; - 即使 A 和 B 共享同一
sdfHolder引用,它们读写互不影响。
注意内存泄漏:及时 remove()
因为 ThreadLocalMap 的 key 是弱引用,但 value 是强引用,如果线程长期运行(如线程池中的线程),而没调用 remove(),value 无法被回收:
立即学习“Java免费学习笔记(深入)”;
- 推荐在使用完后显式调用
sdfHolder.remove(); - 尤其在线程复用场景(如 Servlet 容器、自定义线程池)中必须清理;
- 若用
withInitial(),首次get()会自动初始化,但后续仍需remove()避免堆积。
扩展:想存多个变量?用一个对象封装
ThreadLocal 只管理一个变量,但你可以让它持有自定义对象,从而“逻辑上”存多个字段:
static ThreadLocal<UserContext> context = ThreadLocal.withInitial(UserContext::new);
static class UserContext {
String userId;
String token;
long startTime;
}
- 每个线程有自己的
UserContext实例; - 通过
context.get().userId = "u1001"设置,安全且隔离; - 比声明多个
ThreadLocal更简洁,也避免 map 扩容开销。

















