ThreadLocal 父子线程之间如何传递数据

admin9个月前笔记67

ThreadLocal 的具体原理这篇文章就不解释了,能干啥大伙儿都倒背如流,其实就两点:

  • 链路透传(通俗来说就是方便做参数传递,不用在调用方法时携带一堆请求参数)

  • 线程隔离

每个线程都有自己的一个 ThreadLocalMap,ThreadLocal 持有的数据就是存在这个 Map 里的(Thread.ThreadLocalMap threadLocals),所以能够实现线程隔离,毕竟每个线程的 ThreadLocalMap 都是不一样的

那如果子线程想要拿到父线程的中的 ThreadLocal 值怎么办呢?

比如会有以下的这种代码的实现。在子线程中调用 get 时,我们拿到的 Thread 对象是当前子线程对象,对吧,每个线程都有自己独立的 ThreadLocal,那么当前子线程的 ThreadLocalMap 是 null 的(而父线程,也就是 main 线程中的 ThreadLocalMap 是有数据的),所以我们得到的 value 也是 null

public class ThreadLocalTest {
 private static ThreadLocalthreadLocal = new ThreadLocal();

    public static void main(String[] args) throws  Exception{
        threadLocal.set("飞天小牛肉");
        System.out.println("父线程的值:"+ threadLocal.get());
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程的值:"+ threadLocal.get());
            }
        }).start();

        Thread.sleep(2000);
    }
}

结果输出如下:

父线程的值:飞天小牛肉
子线程的值:null

要如何解决这个问题呢?

我们先来从 Thread 类中找找思路:

image.png

你会发现,在 ThreadLocalMap threadLocals 的下方,还有一个 ThreadLocalMap 变量 inherittableThreadLocals,inherit 翻译为继承

先看下这个变量的注释:InheritableThreadLocal values pertaining to this thread. This map is maintained by the InheritableThreadLocal class.

oho,这里出现了一个渣渣辉都从未体验过的传新类:InheritableThreadLocal

翻译一下注释,大概就是,如果你使用 InheritableThreadLocal,那么保存的数据都已经不在原来的 ThreadLocal.ThreadLocalMap threadLocals 里面了,而是在一个新的 ThreadLocal.ThreadLocalMap inheritableThreadLocals 变量中了。

image.png

所以,如果想让上面那段代码中,子线程能够拿到父线程的 ThreadLocal 值,只需要把 ThreadLocal 声明改为 InheritableThreadLocal 就可以了

下面我们具体来看下 InheritableThreadLocal 是怎么做到父子线程传值的。

首先看下 new Thread 的时候线程都做了些什么 Thread#init()

private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc){
 // 省略部分代码
 Thread parent = currentThread();
     
    if (inheritThreadLocals && parent.inheritableThreadLocals != null)
        // copy父线程的 map,创建一个新的 map 赋值给当前线程的inheritableThreadLocals
  this.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
       
 // 省略部分代码
   }

核心其实就是上面几句代码,如果你设置了 inheritableThreadLocals 变量,那么 Thread 就会把父线程 ThreadLocal threadLocals 中的所有数据都 copy 到子线程的 InheritableThreadLocal inheritableThreadLocals 中。

而且,copy 调用的 createInheritedMap 方法其实是一个浅拷贝函数,key 和 value 都是原来的引用地址,这里所谓的 copy 其实就是把一个 Map 中的数据复制到另一个 Map 中:

image.png

至此,大致的解释了 InheritableThreadLocal 为什么能解决父子线程传递 Threadlcoal 值的问题了,总结下:

在创建InheritableThreadLocal 对象的时候赋值给线程的 t.inheritableThreadLocals 变量。

在创建新线程的时候会 check 父线程中 t.inheritableThreadLocals 变量是否为 null,如果不为 null 则 copy 一份数据到子线程的 t.inheritableThreadLocals 成员变量中去。

InheritableThreadLocal 重写了 getMap(Thread) 方法,所以 get 的时候,就会从 t.inheritableThreadLocals 中拿到 ThreadLocalMap 对象,从而实现了可以拿到父线程 ThreadLocal 中的值。


相关文章

Git忽略规则:.gitignore配置

.gitignore文件一般来说每个Git项目中都需要一个“.gitignore”文件,这个文件的作用就是告诉Git哪些文件不需要添加到版本管理中。实际项目中,很多文件都是不需要版本管理的,比如Pyt...

如何在Centos7配置ssh/rsh免密互信集群服务

如何在Centos7配置ssh/rsh免密互信集群服务

ssh免密互信操作一、在SSH服务器所在机器上1、以root用户登录,更改ssh配置文件 /etc/ssh/sshd_config,去除以下配置的注释RSAAuthentication ye...

用过GPT-4 Turbo以后,我们再也回不去了

用过GPT-4 Turbo以后,我们再也回不去了

前阵子,很多人彻夜未眠 —— 全球科技圈都把目光聚焦在了美国旧金山。短短 45 分钟时间里,OpenAI CEO 山姆・奥特曼向我们介绍了迄今为止最强的大模型,和基于它的一系列应用,一切似乎就像当初...

Linux下如何实现与Internet时间同步

一、安装ntp[root@server-2 ~]# yum install -y ntpdate二、同步时间// 方式一、使用域名连接,要经...

从零开始的科学上网指南

从零开始的科学上网指南

科学上网是当代青年的必修课。由于某些神秘的东方力量的影响,神州大地已与外界隔着一层可悲的厚障壁了。然而,邪恶的境外势力的糖衣炮弹总是蛊惑着数不胜数的懵懂少年,让他们时时不满足于四角的天空。上帝或许将大...

如何获得Open ai key

如何获得Open ai key

打开platform.openai.com,输入你的OpenAI帐号和密码(也就是你的ChatGPT帐号),如果没有就注册一个。登录以后,点屏幕右上角的图标,打开下拉菜单,如图:点击菜单中的“View...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。