InheritableThreadLocal详解
本文内容:
InheritableThreadLocal
可以做什么
InheritableThreadLocal
使用实例
InheritableThreadLocal
原理
InheritableThreadLocal
和线程池搭配使用的问题。
1. InheritableThreadLocal可以做什么
我们知道ThreadLocal解决的是让每个线程读取的ThreadLocal变量是相互独立的。通俗的讲就是,比如我在线程1中set了ThreadLocal的值,那我在线程2中是get不到线程1设置的值的,只能读到线程2自己set的值。
ThreadLocal有一个需求不能满足:就是子线程无法直接复用父线程的ThreadLocal变量里的内容。demo如下:
package com.mt; public class TestThreadLocal implements Runnable { private static ThreadLocal<String> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { System.out.println("----主线程设置值为\"主线程\""); threadLocal.set("主线程"); System.out.println("----主线程设置后获取值:" + threadLocal.get()); Thread tt = new Thread(new TestThreadLocal()); tt.start(); System.out.println("----主线程结束"); } @Override public void run() { System.out.println("----子线程设置值前获取:" + threadLocal.get()); System.out.println("----新开线程设置值为\"子线程\""); threadLocal.set("子线程"); System.out.println("----新开的线程设置值后获取:" + threadLocal.get()); } }
运行结果:
可以看到虽然在main
线程中启动了一个新的子线程,但是threadlocal
变量的内容并没有传递到新的子线程中。
于是乎,InheritableThreadLocal
就出现了。他可以实现在子线程中使用父线程中的线程本地变量(也即InheritableThreadLocal
变量)。
2. InheritableThreadLocal使用实例
根据上面的threadlocal
测试代码稍作修改,把Threadlocal
换做InheritableThreadLocal
。
package com.mt; public class TestInheritableThreadLocal implements Runnable { private static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>(); public static void main(String[] args) { System.out.println("----主线程设置值为\"主线程\""); threadLocal.set("主线程"); System.out.println("----主线程设置后获取值:" + threadLocal.get()); Thread tt = new Thread(new TestInheritableThreadLocal()); tt.start(); System.out.println("----主线程结束"); } @Override public void run() { System.out.println("----子线程设置值前获取:" + threadLocal.get()); System.out.println("----新开线程设置值为\"子线程\""); threadLocal.set("子线程"); System.out.println("----新开的线程设置值后获取:" + threadLocal.get()); } }
运行结果如下:
在子线程设置值之前,就已经能够get
到主线程设置的值了,说明在父子进制之间传递了InheritableThreadLocal
变量。
3.InheritableThreadLocal原理
通过观察InheritableThreadLocal
代码Structure,看到只是重写了ThreadLocal的三个方法:
childValue
,createMap
,getMap
。
我们进入到createMap
方法中查看。
可以看到,InheritableThreadLocal
其实也是用ThreadLocalMap
去存放值,这点和ThreadLocal
一样,只不过InheritableThreadLocal
的变量在Thread类里的名字叫inheritableThreadLocals
。我们进到Thread类中看这个变量。
当我们在主线程start一个子线程时,会new 一个Thread。所以我们要追到Thread类中,看看创建线程时发生了什么才让父子线程的InheritableThreadLocal
可以传递。
首先我们调用的是Thread(Runnable target)
这个方法。
这个方法会调用init
方法,然后经过一系列init
函数重载,最终来到下面这个init
方法。
在这个init
方法里 ,跟InheritableThreadLocal
紧密相关的有下面这些代码:
重点就是if里面的逻辑。
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
第一项inheritThreadLocals
是传进来的boolean
值,重载时传的是true,所以满足条件。
第二项就是判断父线程中的inheritableThreadLocals
是不是空,如果不是空就满足条件。
当同时满足if的两个条件后,就执行
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
新创建出来的子线程的inheritableThreadLocals
变量就和父线程的inheritableThreadLocals
的内容一样了。
以上就是从源码的角度分析InheritableThreadLocal
的原理。
4.InheritableThreadLocal和线程池搭配使用的问题
首先给出结论:
InheritableThreadLocal
和线程池搭配使用时,可能得不到想要的结果,因为线程池中的线程是复用的,并没有重新初始化线程,InheritableThreadLocal
之所以起作用是因为在Thread类中最终会调用init()
方法去把InheritableThreadLocal
的map复制到子线程中。由于线程池复用了已有线程,所以没有调用init()
方法这个过程,也就不能将父线程中的InheritableThreadLocal
值传给子线程。
下面是DEMO:
package com.mt; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class TestInheritableThreadLocalAndExecutor implements Runnable { private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>(); private static ExecutorService executorService = Executors.newFixedThreadPool(1); public static void main(String[] args) throws Exception{ System.out.println("----主线程启动"); inheritableThreadLocal.set("主线程第一次赋值"); System.out.println("----主线程设置后获取值:" + inheritableThreadLocal.get()); executorService.submit(new TestInheritableThreadLocalAndExecutor()); System.out.println("主线程休眠2秒"); Thread.sleep(2000); inheritableThreadLocal.set("主线程第二次赋值"); executorService.submit(new TestInheritableThreadLocalAndExecutor()); executorService.shutdown(); } @Override public void run() { System.out.println("----子线程获取值:" + inheritableThreadLocal.get()); } }
运行结果:
从上图可以看出,我们在main线程
中第二次set
并没有被第二次submit
的线程get
到。也印证了我们的结论。
转:https://blog.csdn.net/yexiguafu/article/details/103900568
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix