找回密码
 立即注册
首页 业界区 业界 InheritableThreadLocal,从入门到放弃

InheritableThreadLocal,从入门到放弃

瞧厨 2026-1-19 13:50:00
InheritableThreadLocal相比ThreadLocal多一个能力:在创建子线程Thread时,子线程Thread会自动继承父线程的InheritableThreadLocal信息到子线程中,进而实现在在子线程获取父线程的InheritableThreadLocal值的目的。
关于ThreadLocal详细内容,可以看这篇文章:史上最全ThreadLocal 详解
和 ThreadLocal 的区别

举个简单的栗子对比下InheritableThreadLocal和ThreadLocal:
  1. public class InheritableThreadLocalTest {    
  2.         private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();    
  3.         private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();    
  4.         public static void main(String[] args) {       
  5.                 testThreadLocal();       
  6.                 testInheritableThreadLocal();   
  7.         }    
  8.         /**     * threadLocal测试     */    
  9.         public static void testThreadLocal() {       
  10.                  // 在主线程中设置值到threadLocal       
  11.                  threadLocal.set("我是父线程threadLocal的值");        
  12.                  // 创建一个新线程并启动        
  13.                  new Thread(() -> {            
  14.                                  // 在子线程里面无法获取到父线程设置的threadLocal,结果为null           
  15.                                  System.out.println("从子线程获取到threadLocal的值: " + threadLocal.get());           }
  16.                  ).start();   
  17.          }    
  18.  
  19.          /**     * inheritableThreadLocal测试     */ 
  20.         public static void testInheritableThreadLocal() {        
  21.                 // 在主线程中设置一个值到inheritableThreadLocal       
  22.                 inheritableThreadLocal.set("我是父线程inheritableThreadLocal的值");        
  23.                 // 创建一个新线程并启动        
  24.                 new Thread(() -> {            
  25.                                 // 在子线程里面可以自动获取到父线程设置的inheritableThreadLocal   
  26.                                 System.out.println("从子线程获取到inheritableThreadLocal的值: " + inheritableThreadLocal.get());       
  27.                         }).start();   
  28.                 }
  29.         }
复制代码
执行结果:
  1. 从子线程获取到threadLocal的值:null
  2. 从子线程获取到inheritableThreadLocal的值:我是父线程inheritableThreadLocal的值
复制代码
可以看到子线程中可以获取到父线程设置的inheritableThreadLocal值,但不能获取到父线程设置的threadLocal值
实现原理

InheritableThreadLocal 的实现原理相当精妙,它通过在创建子线程的瞬间,“复制”父线程的线程局部变量,从而实现了数据从父线程到子线程的一次性、创建时的传递 。
其核心工作原理可以清晰地通过以下序列图展示,它描绘了当父线程创建一个子线程时,数据是如何被传递的:
sequenceDiagram    participant Parent as 父线程    participant Thread as Thread构造方法    participant ITL as InheritableThreadLocal    participant ThMap as ThreadLocalMap    participant Child as 子线程    Parent->>Thread: 创建 new Thread()    Note over Parent,Thread: 关键步骤:初始化    Thread->>Thread: 调用 init() 方法    Note over Thread,ITL: 检查父线程的 inheritableThreadLocals    Thread->>+ThMap: createInheritedMap(
parent.inheritableThreadLocals)    ThMap->>ThMap: 新建一个ThreadLocalMap    loop 遍历父线程Map中的每个Entry        ThMap->>+ITL: 调用 key.childValue(parentValue)        ITL-->>-ThMap: 返回子线程初始值
(默认返回父值,可重写)        ThMap->>ThMap: 将 (key, value) 放入新Map    end    ThMap-->>-Thread: 返回新的ThreadLocalMap对象    Thread->>Child: 将新Map赋给子线程的
inheritableThreadLocals属性    Note over Child: 子线程拥有父线程变量的副本下面我们来详细拆解图中的关键环节。
核心实现机制


  • **数据结构基础: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获取到的值为null:
  1. public class InheritableThreadLocalWithThreadPoolTest {    
  2.         private static final InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();    
  3.         // 这里线程池core/max数量都只有2    
  4.         private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(            
  5.                 2,            
  6.                 2,            
  7.                 0L,           
  8.                 TimeUnit.MILLISECONDS,            
  9.                 new LinkedBlockingQueue<Runnable>(3000),            
  10.                 new ThreadPoolExecutor.CallerRunsPolicy()   
  11.         );    
  12.        
  13.         public static void main(String[] args) {        
  14.         // 先执行了不涉及InheritableThreadLocal的子任务初始化线程池线程 
  15.                testAnotherFunction();       
  16.                testAnotherFunction();        
  17.                // 后执行了涉及InheritableThreadLocal
  18.                testInheritableThreadLocalWithThreadPool("张三");       
  19.                testInheritableThreadLocalWithThreadPool("李四");       
  20.                threadPoolExecutor.shutdown();   
  21.          }    
  22.          
  23.          /**     * inheritableThreadLocal+线程池测试     */    
  24.             public static void testInheritableThreadLocalWithThreadPool(String param) {        
  25.                     // 1. 在主线程中设置一个值到inheritableThreadLocal       
  26.                  inheritableThreadLocal.set(param);        
  27.                 // 2. 提交异步任务到线程池       
  28.                 threadPoolExecutor.execute(() -> {            
  29.                 // 3. 在线程池-子线程里面可以获取到父线程设置的inheritableThreadLocal吗?           
  30.                         System.out.println("线程名: " + Thread.currentThread().getName() + ", 父线程设置的inheritableThreadLocal值: " + param + ", 子线程获取到inheritableThreadLocal的值: " + inheritableThreadLocal.get());       
  31.                 });        
  32.                 // 4. 清除inheritableThreadLocal       
  33.                 inheritableThreadLocal.remove();   
  34.            }    
  35.                       
  36.            /**     * 模拟另一个独立的功能     */   
  37.            public static void testAnotherFunction() {        
  38.                    // 提交异步任务到线程池       
  39.                threadPoolExecutor.execute(() -> {            
  40.                // 在线程池-子线程里面可以获取到父线程设置的inheritableThreadLocal吗?           
  41.                        System.out.println("线程名: " + Thread.currentThread().getName() + ", 线程池-子线程摸个鱼");       
  42.                });   
  43.            }
  44. }
复制代码
执行结果:
  1. 线程名:pool-1-thread-2,线程池-子线程摸个鱼
  2. 线程名:pool-1-thread-1,线程池-子线程摸个鱼
  3. 线程名:pool-1-thread-1,父线程设置的inheritableThreadLocal值:李四,子线程获取到inheritableThreadLocal的值:null
  4. 线程名:pool-1-thread-2,父线程设置的inheritableThreadLocal值:张三,子线程获取到inheritableThreadLocal的值:null
复制代码
当然了,解决这个问题可以考虑使用阿里开源的 TransmittableThreadLocal (TTL),​或者在提交异步任务前,先获取线程数据,再传入。例如:
  1. // 1. 在主线程中先获取inheritableThreadLocal的值
  2. String name = inheritableThreadLocal.get();   
  3.     
  4. // 2. 提交异步任务到线程池       
  5. threadPoolExecutor.execute(() -> {            
  6. // 3. 在线程池-子线程里面直接传入数据 
  7. System.out.println("线程名: " + Thread.currentThread().getName() + ", 父线程设置的inheritableThreadLocal值: " + param + ", 子线程获取到inheritableThreadLocal的值: " + name);       
  8.                 });        
复制代码
与 ThreadLocal 的对比

特性ThreadLocalInheritableThreadLocal数据隔离​线程绝对隔离线程绝对隔离子线程继承不支持支持(创建时)底层存储字段​Thread.threadLocalsThread.inheritableThreadLocals适用场景​线程内全局变量,避免传参父子线程间需要传递上下文数据
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

2026-2-5 06:05:13

举报

2026-2-6 06:33:16

举报

2026-2-12 08:25:45

举报

2026-2-13 18:09:27

举报

喜欢鼓捣这些软件,现在用得少,谢谢分享!
您需要登录后才可以回帖 登录 | 立即注册