当前位置: 首页 > news >正文

ThreadLocal--ThreadLocal介绍

🧠 一、什么是 ThreadLocal

  • ThreadLocal 是 Java 提供的一种 线程本地变量机制

  • 每个线程都维护一份自己的副本;

  • 它不用于多个线程共享变量,而是用于每个线程独立维护自己的变量副本

  • 常用于:用户上下文、数据库连接、格式化对象(如 SimpleDateFormat)、日志跟踪等场景。


🚀 二、ThreadLocal 的基本用法

ThreadLocal<String> local = new ThreadLocal<>();
local.set("hello");      // 设置当前线程的副本
String val = local.get(); // 获取当前线程的副本
local.remove();           // 手动删除,防止内存泄漏

每个线程访问的是 自己的变量副本,彼此隔离。


🧱 三、ThreadThreadLocalThreadLocalMap 三者关系图

Thread (线程对象)└── ThreadLocalMap (每个线程独有的 map)└── Entry[] 数组├── key:ThreadLocal 对象(弱引用)└── value:真正的变量值

✅ 总结对应关系:

角色说明
Thread每个线程都有一个 ThreadLocalMap
ThreadLocal作为 key 存在于 ThreadLocalMap 中,指向当前线程的副本
ThreadLocalMapThread 内部的属性,负责存储每个 ThreadLocal 对应的数据

🎯 四、为什么 ThreadLocalMap 的 key 是 弱引用

这个想要了解更详细可以看博主的另一篇博客:ThreadLocal--ThreadLocal 竟可能导致内存泄漏?看看 ThreadLocalMap 的弱引用机制-CSDN博客

✅ Java 源码:

static class Entry extends WeakReference<ThreadLocal<?>> {Object value;
}

✅ 原因:防止内存泄漏(重点)

  • 如果 ThreadLocal强引用

    • 即使我们不再使用 ThreadLocal,它依然会作为 key 强引用存在,永远不会被 GC;

    • 而且 ThreadLocalMap 属于 ThreadThread 不结束就不会释放内存;

    • 久而久之,value 也无法回收,造成 内存泄漏

✅ 如果是 弱引用

  • 当开发者不再持有 ThreadLocal 引用时,它会被 GC 回收;

  • GC 后 ThreadLocalMap 中 key 为 null;

  • 如果调用 ThreadLocal.get() / set(),会清除掉这些 stale entry(陈旧数据);

  • ✅ 避免内存泄漏。


💣 五、ThreadLocal 内存泄漏陷阱

  • 问题场景:

    • 线程池中线程长时间不销毁;

    • ThreadLocal 被 GC 回收,但 ThreadLocalMap 的 value 还存在;

    • 如果不调用 .remove(),value 永远不会清理;

  • 解决方式:

    • ✅ 使用完后调用 ThreadLocal.remove() 清理;

    • ✅ 或者用 try-finally 包装使用逻辑:

private static final ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd"));public void parseDate(String dateStr) {try {formatter.get().parse(dateStr);} finally {formatter.remove(); // 手动清除,避免内存泄漏}
}

🧰 六、ThreadLocalMap 的实现细节

  • 本质上是一个自定义的哈希表:

    • 数组结构 + 开放寻址法(冲突后线性探测)

  • 不是 HashMap,也不是 ConcurrentHashMap

  • 数组大小默认 16,按需扩容(最多 2^30)


🛠 七、常用方法详解

方法说明
set(T value)设置当前线程副本中的值
get()获取当前线程副本中的值
remove()删除当前线程副本中的值
withInitial(Supplier)构造带默认初始值的 ThreadLocal


✅ 示例:使用默认初始值的 ThreadLocal

ThreadLocal<Integer> counter = ThreadLocal.withInitial(() -> 0);public void increment() {counter.set(counter.get() + 1);
}

🌟 八、InheritableThreadLocal:子线程继承父线程值

InheritableThreadLocal<String> local = new InheritableThreadLocal<>();
local.set("父线程值");new Thread(() -> {System.out.println(local.get()); // 子线程能读取父线程设置的值
}).start();

适合:父线程传递上下文,如用户ID、请求ID 等。


🧭 九、实际应用场景

场景示例
✅ 用户上下文登录后存放用户信息:ThreadLocal<User>
✅ DateFormatSimpleDateFormat 非线程安全,放入 ThreadLocal
✅ 数据源切换动态数据源管理,存放在 ThreadLocal
✅ Trace ID日志链路追踪,全链路唯一 ID 存 ThreadLocal
✅ Spring事务/安全Spring 的 TransactionSynchronizationManagerSecurityContextHolder 都用到了 ThreadLocal

📌 十、总结

项目内容
本质每个线程一个变量副本
原理每个线程有一个 ThreadLocalMap
结构key 为弱引用的 ThreadLocal,value 为副本值
弱引用原因防止内存泄漏,GC 后 key=null 自动清理
使用建议用完及时调用 remove()
延伸功能InheritableThreadLocal 实现值传递
应用场景用户信息、日期格式化、日志追踪、数据库连接等
http://www.dtcms.com/a/299846.html

相关文章:

  • 7.26 cpu
  • 单片机ADC机理层面详细分析(一)
  • SSE (Server-Sent Events) 服务出现连接卡在 pending 状态的原因
  • 嵌入式软硬件开发入门工具推荐
  • `read`系统调用示例
  • java每日精进 7.26【流程设计5.0(中间事件+结束事件)】
  • 检索召回率优化探究一:基于 LangChain 0.3集成 Milvus 2.5向量数据库构建的智能问答系统
  • 全球化2.0 | 云轴科技ZStack亮相阿里云印尼国有企业CXO专家活动
  • FreeMarker模板引擎
  • Windows Server系统安装JDK,一直卡在“应用程序正在为首次使用作准备,请稍候”
  • Vibe Coding | 技术让我们回归了创造的本质
  • hot100-每日温度
  • 字符串缓冲区和正则表达式
  • I/O 软件层次结构
  • 分布式数据库的分布透明性详解
  • 【前端】Vue 3 课程选择组件开发实战:从设计到实现
  • 如何从自定义或本地仓库安装 VsCode 扩展
  • 手写PPO_clip(FrozenLake环境)
  • 统计学08:概率分布
  • 面试实战,问题十二,Spring Boot接收和处理HTTP请求的详细原理,怎么回答
  • AI 编程工具 Trae 重要的升级。。。
  • 二维数组相关学习
  • 栈----3.字符串解码
  • 论文阅读-RaftStereo
  • 2025中国GEO优化白皮书:AI搜索优化趋势+行业数据报告
  • 应急控制HMI的“黄金10秒”设计:紧急场景下的操作路径极速简化技术
  • 嵌入式硬件篇---有线串口通信问题解决
  • PHP语法高级篇(六):面向对象编程
  • MyBatis-Plus 核心注解详解:从表映射到逻辑删除的全方位指南
  • C++/CLI vs 标准 C++ vs C# 语法对照手册