和 ThreadLocal 的区别
举个简单的栗子对比下InheritableThreadLocal和ThreadLocaljavapublic class InheritableThreadLocalTest { private static final ThreadLocalString threadLocal new ThreadLocal(); private static final InheritableThreadLocalString inheritableThreadLocal new InheritableThreadLocal(); public static void main(String[] args) { testThreadLocal(); testInheritableThreadLocal(); } /** * threadLocal测试 */ public static void testThreadLocal() { // 在主线程中设置值到threadLocal threadLocal.set(我是父线程threadLocal的值); // 创建一个新线程并启动 new Thread(() - { // 在子线程里面无法获取到父线程设置的threadLocal结果为null System.out.println(从子线程获取到threadLocal的值: threadLocal.get()); } ).start(); } /** * inheritableThreadLocal测试 */ public static void testInheritableThreadLocal() { // 在主线程中设置一个值到inheritableThreadLocal inheritableThreadLocal.set(我是父线程inheritableThreadLocal的值); // 创建一个新线程并启动 new Thread(() - { // 在子线程里面可以自动获取到父线程设置的inheritableThreadLocal System.out.println(从子线程获取到inheritableThreadLocal的值: inheritableThreadLocal.get()); }).start(); } }执行结果text从子线程获取到threadLocal的值:null 从子线程获取到inheritableThreadLocal的值:我是父线程inheritableThreadLocal的值可以看到子线程中可以获取到父线程设置的inheritableThreadLocal值但不能获取到父线程设置的threadLocal值实现原理InheritableThreadLocal 的实现原理相当精妙它通过在创建子线程的瞬间“复制”父线程的线程局部变量从而实现了数据从父线程到子线程的一次性、创建时的传递 。其核心工作原理可以清晰地通过以下序列图展示它描绘了当父线程创建一个子线程时数据是如何被传递的子线程ThreadLocalMapInheritableThreadLocalThread构造方法父线程子线程ThreadLocalMapInheritableThreadLocalThread构造方法父线程关键步骤初始化检查父线程的 inheritableThreadLocalsloop[遍历父线程Map中的每个Entry]子线程拥有父线程变量的副本创建 new Thread()调用 init() 方法createInheritedMap(parent.inheritableThreadLocals)新建一个ThreadLocalMap调用 key.childValue(parentValue)返回子线程初始值(默认返回父值可重写)将 (key, value) 放入新Map返回新的ThreadLocalMap对象将新Map赋给子线程的inheritableThreadLocals属性下面我们来详细拆解图中的关键环节。核心实现机制**数据结构基础Thread类内部维护了两个ThreadLocalMap类型的变量 threadLocals用于存储普通ThreadLocal设置的变量副本。inheritableThreadLocals专门用于存储InheritableThreadLocal设置的变量副本 。InheritableThreadLocal通过重写getMap和createMap方法使其所有操作都针对inheritableThreadLocals字段从而与普通ThreadLocal分离开 。继承触发时刻子线程的创建。继承行为发生在子线程被创建即执行new Thread()时。在Thread类的init方法中如果判断需要继承inheritThreadLocals参数为true且父线程当前线程的inheritableThreadLocals不为null则会执行复制逻辑 。复制过程的核心createInheritedMap。这是实现复制的核心方法 。它会创建一个新的ThreadLocalMap并将父线程inheritableThreadLocals中的所有条目遍历拷贝到新 Map 中。Key的复制Key即InheritableThreadLocal对象本身是直接复制的引用。Value的生成Value 并非直接复制引用而是通过调用InheritableThreadLocal的childValue(T parentValue)方法来生成子线程中的初始值。默认实现是直接返回父值return parentValue;这意味着对于对象类型父子线程将共享同一个对象引用 。关键特性与注意事项创建时复制后续独立继承只发生一次即在子线程对象创建的瞬间。此后父线程和子线程对各自InheritableThreadLocal变量的修改互不影响 。在线程池中的局限性这是InheritableThreadLocal最需要警惕的问题。线程池中的线程是复用的这些线程在首次创建时可能已经从某个父线程继承了值。但当它们被用于执行新的任务时新的任务提交线程逻辑上的“父线程”与工作线程已无直接的创建关系因此之前继承的值不会更新这会导致数据错乱如用户A的任务拿到了用户B的信息或内存泄漏​ 。对于线程池场景应考虑使用阿里开源的TransmittableThreadLocal (TTL)​ 。浅拷贝与对象共享由于childValue方法默认是浅拷贝如果存入的是可变对象如Map、List父子线程实际持有的是同一个对象的引用。在一个线程中修改该对象的内部状态会直接影响另一个线程 。若需隔离可以重写childValue方法实现深拷贝 。内存泄漏风险与ThreadLocal类似如果线程长时间运行如线程池中的核心线程并且未及时调用remove方法清理那么该线程的inheritableThreadLocals会一直持有值的强引用导致无法被GC回收。良好的实践是在任务执行完毕后主动调用remove()线程池中局限性一般来说在真实的业务场景下没人会直接 new Thread而都是使用线程池的因此InheritableThreadLocal在线程池中的使用局限性要额外注意首先我们先理解InheritableThreadLocal的继承前提InheritableThreadLocal的继承只发生在新线程被创建时即new Thread()并启动时。在创建过程中子线程会复制父线程的InheritableThreadLocal值。在线程池中线程是预先创建或按需创建的并且会被复用。因此继承只会在线程池创建新线程时发生而不会在复用现有线程时发生。再看线程池创建新线程的条件对于标准的ThreadPoolExecutor新线程的创建遵循以下规则当前线程数 核心线程数当提交新任务时如果当前运行的线程数小于核心线程数即使有空闲线程线程池也会创建新线程来处理任务。此时新线程会继承父线程提交任务的线程的InheritableThreadLocal。当前线程数 核心线程数 队列已满 线程数 最大线程数当任务队列已满且当前线程数小于最大线程数时线程池会创建新线程来处理任务。同样新线程会继承父线程的InheritableThreadLocal。不会继承的场景线程复用当线程池中有空闲线程时例如当前线程数 核心线程数但队列未满任务会被分配给现有线程执行。此时没有新线程创建因此不会发生继承。现有线程的InheritableThreadLocal值保持不变可能是之前任务设置的值这可能导致数据错乱如用户A的任务看到用户B的数据。线程数已达最大值如果线程数已达最大线程数且队列已满新任务会被拒绝根据拒绝策略也不会创建新线程因此不会继承。不只是线程池污染线程池使用InheritableThreadLocal还可能存在获取不到值的情况。例如在执行异步任务的时候复用了某个已有的线程A并且当时创建该线程A的时候没有继承InheritableThreadLocal进而导致后面复用该线程的时候从InheritableThreadLocal获取到的值为nulljavapublic class InheritableThreadLocalWithThreadPoolTest { private static final InheritableThreadLocalString inheritableThreadLocal new InheritableThreadLocal(); // 这里线程池core/max数量都只有2 private static final ThreadPoolExecutor threadPoolExecutor new ThreadPoolExecutor( 2, 2, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueueRunnable(3000), new ThreadPoolExecutor.CallerRunsPolicy() ); public static void main(String[] args) { // 先执行了不涉及InheritableThreadLocal的子任务初始化线程池线程 testAnotherFunction(); testAnotherFunction(); // 后执行了涉及InheritableThreadLocal testInheritableThreadLocalWithThreadPool(张三); testInheritableThreadLocalWithThreadPool(李四); threadPoolExecutor.shutdown(); } /** * inheritableThreadLocal线程池测试 */ public static void testInheritableThreadLocalWithThreadPool(String param) { // 1. 在主线程中设置一个值到inheritableThreadLocal inheritableThreadLocal.set(param); // 2. 提交异步任务到线程池 threadPoolExecutor.execute(() - { // 3. 在线程池-子线程里面可以获取到父线程设置的inheritableThreadLocal吗 System.out.println(线程名: Thread.currentThread().getName() , 父线程设置的inheritableThreadLocal值: param , 子线程获取到inheritableThreadLocal的值: inheritableThreadLocal.get()); }); // 4. 清除inheritableThreadLocal inheritableThreadLocal.remove(); } /** * 模拟另一个独立的功能 */ public static void testAnotherFunction() { // 提交异步任务到线程池 threadPoolExecutor.execute(() - { // 在线程池-子线程里面可以获取到父线程设置的inheritableThreadLocal吗 System.out.println(线程名: Thread.currentThread().getName() , 线程池-子线程摸个鱼); }); } }执行结果text线程名:pool-1-thread-2,线程池-子线程摸个鱼 线程名:pool-1-thread-1,线程池-子线程摸个鱼 线程名:pool-1-thread-1,父线程设置的inheritableThreadLocal值:李四子线程获取到inheritableThreadLocal的值:null 线程名:pool-1-thread-2,父线程设置的inheritableThreadLocal值:张三子线程获取到inheritableThreadLocal的值:null当然了解决这个问题可以考虑使用阿里开源的TransmittableThreadLocal (TTL)​或者在提交异步任务前先获取线程数据再传入。例如java// 1. 在主线程中先获取inheritableThreadLocal的值 String name inheritableThreadLocal.get(); // 2. 提交异步任务到线程池 threadPoolExecutor.execute(() - { // 3. 在线程池-子线程里面直接传入数据 System.out.println(线程名: Thread.currentThread().getName() , 父线程设置的inheritableThreadLocal值: param , 子线程获取到inheritableThreadLocal的值: name); });与 ThreadLocal 的对比

相关新闻

嵌入式软件单元测试在汽车软件开发中举足轻重 —— 权威支撑与工程本质

嵌入式软件单元测试在汽车软件开发中举足轻重 —— 权威支撑与工程本质

摘要电动化、智能化浪潮重构汽车产业底层逻辑,车载嵌入式软件代码规模呈指数级膨胀,高端智能整车软件总量突破 2 亿行,远超民航客机软件体量。软件已成为整车动力控制、制动安全、自动驾驶、能源管理等核心功能的唯一载体,嵌入式软…

2026/7/1 10:38:38阅读更多 →
终极实战指南:用Vite高效构建现代化Chrome扩展程序

终极实战指南:用Vite高效构建现代化Chrome扩展程序

终极实战指南:用Vite高效构建现代化Chrome扩展程序 【免费下载链接】vite-chrome-extension 又快又爽的开发 Chrome 扩展程序 项目地址: https://gitcode.com/gh_mirrors/vi/vite-chrome-extension 在现代浏览器扩展开发领域,vite-chrome-extensi…

2026/7/1 10:38:38阅读更多 →
从UI设计缺陷到RCE:CVE-2025-14404漏洞深度剖析与安全启示

从UI设计缺陷到RCE:CVE-2025-14404漏洞深度剖析与安全启示

1. 项目概述:一次由UI设计缺陷引发的安全风暴最近在安全社区里,一个关于PDF处理工具PDFsam的漏洞讨论热度很高,编号是CVE-2025-14404。乍一看标题,“EnhancedUI设计缺陷导致远程代码执行”,很多人的第一反应可能是&…

2026/7/1 10:38:38阅读更多 →
AI学术事故越来越多!做科研要选懂规则的专业AI,别把通用聊天机器人当主力

AI学术事故越来越多!做科研要选懂规则的专业AI,别把通用聊天机器人当主力

最近这两年,AI闯的学术祸越来越多,还在把通用AI当科研主力用,早晚会踩到大坑!真正适合科研的,从来不是啥都能聊的全能聊天机器人,而是把学术规则刻进底层逻辑的科研专用AI。不少人花大几千冲了通用AI会员&a…

2026/7/1 12:44:48阅读更多 →
MC6470与PIC18LF26K40的硬件架构与运动控制实现

MC6470与PIC18LF26K40的硬件架构与运动控制实现

1. MC6470与PIC18LF26K40的硬件架构解析MC6470是一款六轴运动传感器(3轴加速度计3轴陀螺仪),采用I2C/SPI数字接口,测量范围可编程配置。其核心优势在于内置了运动处理引擎(DMP),能够直接在芯片内…

2026/7/1 12:44:48阅读更多 →
MC6470与MKV42F128VLH16的硬件协同与传感器融合实践

MC6470与MKV42F128VLH16的硬件协同与传感器融合实践

1. MC6470与MKV42F128VLH16的硬件协同架构解析MC6470作为一款六轴惯性测量单元(IMU),集成了三轴加速度计和三轴陀螺仪,其核心优势在于16g的加速度测量范围和2000dps的角速度测量范围。在实际项目中,我通常会优先关注其数字输出接口的配置方式…

2026/7/1 12:44:48阅读更多 →
SLO2016与dsPIC30F4011嵌入式通信方案解析

SLO2016与dsPIC30F4011嵌入式通信方案解析

1. SLO2016与dsPIC30F4011的硬件协同架构解析 SLO2016作为一款专业级数字信号处理器,与Microchip的dsPIC30F4011单片机形成了一套高效的嵌入式通信解决方案。这对组合在工业自动化、远程监测等领域展现出独特优势——SLO2016负责高速信号处理,而dsPIC30F…

2026/7/1 12:44:48阅读更多 →
控制器示例

控制器示例

Controller public class WebSocketController {// 注入消息模板Autowiredprivate SimpMessagingTemplate messagingTemplate;/*** 处理客户端发送的消息* 目的地:/app/chat*/MessageMapping("/chat")SendTo("/topic/messages")public ChatMess…

2026/7/1 12:44:48阅读更多 →
LX Music Desktop:一站式开源音乐播放器的革命性体验

LX Music Desktop:一站式开源音乐播放器的革命性体验

LX Music Desktop:一站式开源音乐播放器的革命性体验 【免费下载链接】lx-music-desktop 一个基于 Electron 的音乐软件 项目地址: https://gitcode.com/GitHub_Trending/lx/lx-music-desktop 在数字音乐时代,我们常常面临一个困境:音…

2026/7/1 12:39:48阅读更多 →
AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

AI Coding 六个月真实ROI账本:产品经理的血泪教训,研发的冷静忠告

6个月前的2025年12月,Boris Cherny 公开宣布自己卸载了 IDE。一时间,Vibe Coding 成了全行业最热的话题。6个月后,当我们回过头来拉一份真实账本,发现事情远没有"一句话生成一个App"那么浪漫。本文从产品经理和研发两个…

2026/7/1 4:42:14阅读更多 →
审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

审计来了,数据权限全开——审计走了,怎么确保权限全部关掉?

引言:审计结束三个月了,审计员的权限还没关某城商行每年按照监管要求开展至少一次数据安全审计。审计期间,内审部门需要抽样检查各类业务数据——交易流水、客户信息、员工操作日志、权限配置记录。这些数据分布在不同系统中,审计…

2026/7/1 5:19:01阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/1 0:01:44阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/1 0:01:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/1 0:01:44阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/1 0:01:44阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/1 0:01:44阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/1 0:01:44阅读更多 →