你问这谁会啊?ThreadLocal 父子线程之间该如何传递数据?

飞天小牛肉

共 3801字,需浏览 8分钟

 · 2022-11-15



忘记之前是哪个公司面试的时候问到的,并不是一个常见的问题,我当时也没回答正确,就按照线程通信那一套比如什么 synchronized、Locks、volatile 啥的 XJB 说的,面试完找了些资料今天整理了下分享给大家~

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

  1. 链路透传(通俗来说就是方便做参数传递,不用在调用方法时携带一堆请求参数)
  2. 线程隔离

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

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

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

public class ThreadLocalTest {
 private static ThreadLocal<String> threadLocal = new ThreadLocal<String>();

    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 类中找找思路:

你会发现,在 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 变量中了。

所以,如果想让上面那段代码中,子线程能够拿到父线程的 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 中:

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

  1. 在创建 InheritableThreadLocal 对象的时候赋值给线程的 t.inheritableThreadLocals 变量
  2. 在创建新线程的时候会 check 父线程中 t.inheritableThreadLocals 变量是否为 null,如果不为 null 则 copy 一份数据到子线程的 t.inheritableThreadLocals 成员变量中去
  3. InheritableThreadLocal 重写了 getMap(Thread) 方法,所以 get 的时候,就会从 t.inheritableThreadLocals 中拿到 ThreadLocalMap 对象,从而实现了可以拿到父线程 ThreadLocal 中的值




长风破浪会有时,小伙伴们下篇文章再见
回复『春秋招』我拉你进交流群



浏览 26
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报