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

ThreadLocal使用及其原理和注意点

关键注意事项

1.必须调用 remove () 方法
线程池中的线程是复用的,如果不清除,下次复用线程时会读到旧数据,导致逻辑错误或内存泄漏。

2.避免使用 static 滥用
虽然 ThreadLocal 常声明为 static,但需明确其作用域,避免存储过大对象。

3.为什么要使用 static 修饰

如果 ThreadLocal 是非静态的(实例变量),那么每个类实例都会创建一个独立的ThreadLocal 对象。这会导致:

同一个线程中,通过不同的类实例访问 ThreadLocal 时,拿到的是不同容器中的数据(不符合 "线程内全局共享" 的预期)。

例如:UserContext 类的两个实例 ctx1ctx2,它们的非静态 ThreadLocal 会让线程 A 在 ctx1 存的数据,在 ctx2 中读不到。

4.父子线程数据不共享
子线程无法读取父线程的 ThreadLocal 数据,如需共享可使用 InheritableThreadLocal

注意:可以创建多份 ThreadLocal

一个线程中可能同时需要存储:

  • 用户登录信息(User 类型)
  • 数据库事务 ID(String 类型)
  • 本次请求的日志追踪 ID(Long 类型)

此时需要定义多个 ThreadLocal

public class ThreadContext {// 存储用户信息private static ThreadLocal<User> userLocal = new ThreadLocal<>();// 存储事务IDprivate static ThreadLocal<String> transactionIdLocal = new ThreadLocal<>();// 存储日志追踪IDprivate static ThreadLocal<Long> traceIdLocal = new ThreadLocal<>();// 用户信息的get/set/removepublic static void setUser(User user) {userLocal.set(user);}public static User getUser() {return userLocal.get();}public static void removeUser() {userLocal.remove();}// 事务ID的get/set/removepublic static void setTransactionId(String id) {transactionIdLocal.set(id);}public static String getTransactionId() {return transactionIdLocal.get();}public static void removeTransactionId() {transactionIdLocal.remove();}// 日志追踪ID的get/set/removepublic static Long getTraceId() {return traceIdLocal.get();}public static void setTraceId(Long id) {traceIdLocal.set(id);}public static void removeTraceId() {traceIdLocal.remove();}
}

使用上:

// 存储数据
ThreadContext.setUser(new User("张三"));
ThreadContext.setTransactionId("tx-123456");
ThreadContext.setTraceId(10086L);// 读取数据(同一线程内)
User user = ThreadContext.getUser(); // 张三
String txId = ThreadContext.getTransactionId(); // tx-123456

总结

  1. 静态声明 ThreadLocal:是为了保证容器实例唯一,避免资源浪费,确保线程内变量的全局一致性(最常见的使用方式)。
  2. 多个 ThreadLocal 实例:完全合理且必要,用于隔离同一线程中的不同类型变量(如用户信息、事务 ID 等)。

核心原则:一个 ThreadLocal 实例对应一种类型的线程变量,静态声明是为了让这种对应关系全局唯一。

 

 

http://www.dtcms.com/a/298080.html

相关文章:

  • 背包DP之完全背包
  • MCP (Model Context Protocol) 与 HTTP API:大模型时代的通信新范式
  • 如何在windows设置Redis服务后台自启动
  • Hive【安装 01】hive-3.1.2版本安装配置(含 mysql-connector-java-5.1.47.jar 网盘资源)
  • 数据管理能力成熟度评估模型(DCMM)详解
  • 基于Matlab图像处理的瓶子自动检测与质量评估系统
  • SpringBoot整合Fastexcel/EasyExcel导出Excel导出多个图片
  • QKV 为什么是三个矩阵?注意力为何要除以 √d?多头注意力到底有啥用?
  • MyBatis 之缓存机制核心解析
  • android JXL 导出Excel(.xls/xlsx)
  • 解决企业微信收集表没有图片、文件组件,不能收集图片的问题
  • windows 安排 openssl
  • 三、操作系统——第1章:计算机系统概述
  • 星痕共鸣 C++显示打出的攻击力
  • 【前端工程化】前端项目开发过程中如何做好通知管理?
  • AVL树和红黑树的特性以及模拟实现
  • 【CMake】CMake 与 C++ 协同:条件配置机制及控制台控制实例解析
  • [C++]string::substr
  • MindJourney:构建空间智能的新范式——VLM与视频扩散式世界模型的融合
  • 【LeetCode Solutions】LeetCode 热题 100 题解(16 ~ 20)
  • 【牛客网C语言刷题合集】(三)
  • 2025年-ClickHouse 高性能实时分析数据库(大纲版)
  • 【开发杂谈】用AI玩AI聊天游戏:使用 Electron 和 Python 开发大模型语音聊天软件
  • 如何搭建Linux环境下的flink本地集群
  • 【硬件-笔试面试题】硬件/电子工程师,笔试面试题-26,(知识点:硬件电路的调试方法:信号追踪,替换,分段调试)
  • 飞算 JavaAI “撤回接口信息” 功能:误删接口不用慌,一键恢复更省心
  • Linux 设备驱动模型
  • WINDOWS10系统重装软件篇
  • QML图形效果之阴影效果(DropShadow与InnerShadow)
  • Cacti命令执行漏洞分析(CVE-2022-46169)