InheritableThreadLocal的使用与原理

经过 ThreadLocal实现原理 这一篇的分析。我们了解了ThreadLocal的原理,也知道我们可以使用ThreadLocal来保存线程的局部变量,但是我们也知道,一个线程保存的值,另外一个线程也是访问不了的,那么问题也随之而来了,比如我们有时候想要在子线程中访问父线程中ThreadLoca中的值,那么该怎么办呢?

InheritableThreadLocal

使用InheritableThreadLocal就能做到,这个类是ThreadLocal的子类,我们来看下它的实现

public class InheritableThreadLocal<T> extends ThreadLocal<T> {

    protected T childValue(T parentValue) {
        return parentValue;
    }

    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

继承了ThreadLocal,并且重写了其中的三个方法

我们先来看下怎么使用InheritableThreadLocal,以便于我们更好的介绍这个类的实现

 public void testInheritableThreadLocal() throws InterruptedException {
      InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
      threadLocal.set("MainThread");

      Thread thread = new Thread(() -> System.out.println("子线程取出的值" + threadLocal.get()));
      thread.start();
      System.out.println("父线程取出的值" + threadLocal.get());
      thread.join();
   }


执行结果

在这里插入图片描述

我们先从这行代码开始介绍

	threadLocal.set("MainThread");

具体实现

 public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

这里在我之前那篇ThreadLocal原理中就分析过这个方法。这里拿出来说是要你们注意的是,getMap(t)和createMap(t, value) 这两个方法是被InheritableThreadLocal重写来的,所以它调用的应该是这两个实现


  ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }

同样,inheritableThreadLocals这个属性也被定义在Thread中
在这里插入图片描述

当我们第一次调用set(t)这个方法的时候会初始化这个变量具体看void createMap(Thread t, T firstValue)这个方法,上面有把这个实现贴出来

接着我们来看这一行代码

Thread thread = new Thread(
		() -> System.out.println("子线程取出的值" + threadLocal.get()));

这一行就是创建了一个Thread,并且去InheritableThreadLocal中去取值,那么它是怎么取到值的呢?
我们先来看 new Thread() 到底发生了什么,跟着Thread的构造函数进来,找到这个构造函数

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize, AccessControlContext acc) {
        if (name == null) {
            throw new NullPointerException("name cannot be null");
        }

        this.name = name.toCharArray();
		
		// 把当前创建这个线程的线程的作为父线程
        Thread parent = currentThread();
        SecurityManager security = System.getSecurityManager();
        if (g == null) {
           
            if (security != null) {
                g = security.getThreadGroup();
            }

           
            if (g == null) {
                g = parent.getThreadGroup();
            }
        }

      
        g.checkAccess();

        
        if (security != null) {
            if (isCCLOverridden(getClass())) {
                security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
        }

        g.addUnstarted();

        this.group = g;
        //  这里稍微提一下,如果当前线程的父线程是守护线程的话那么这个子线程也将是守护进程
        this.daemon = parent.isDaemon();
        this.priority = parent.getPriority();
        if (security == null || isCCLOverridden(parent.getClass()))
            this.contextClassLoader = parent.getContextClassLoader();
        else
            this.contextClassLoader = parent.contextClassLoader;
        this.inheritedAccessControlContext =
                acc != null ? acc : AccessController.getContext();
        this.target = target;
        setPriority(priority);
        // 如果父线程中的inheritableThreadLocal不为null
        // 则创建一个新的 ThreadLocalMap对象,并且把父线程中 inheritableThreadLocal
        // 的值复制到 当前线程的 inheritableThreadLocal中
        if (parent.inheritableThreadLocals != null)
            this.inheritableThreadLocals =
                ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
       
        this.stackSize = stackSize;

        
        tid = nextThreadID();
    }

为了你们能够方便找到这段代码,以及在这里能够方便查看,我把原来的英文注释给去掉了,只留下了我写的注释
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);的实现

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
		// 创建一个新的ThreadLocalMap对象并把父线程中ThreadLocalMap中的值复制到这个对象中
        return new ThreadLocalMap(parentMap);
    }

然后接下来我们来查看这一行代码中的 threadLocal.get()方法。看它是怎么拿到值的
相信大部分人到这里应该明白了是怎么拿到值的。不明白的也没有关系。我们接着看

Thread thread = new Thread(
() -> System.out.println("子线程取出的值" + threadLocal.get()));

同样这段代码我们也在ThreadLocal的原理中分析过,仍然值得我们注意的是 getMap(t)是调用的InheritableThreadLocal类的

    public T get() {
        Thread t = Thread.currentThread();
        //这里调用的是InheritableThreadLocal类的getMap(t)
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

InheritableThreadLocal类中的getMap(t)方法就是返回当前线程中inheritableThreadLocals这个属性,而当我们new Thread()的时候就会把父线程中的inheritableThreadLocals属性中的值拿出来并复制到一个新的ThreadLocalMap对象中

ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }

总结

对于上面这个例子,当我们创建InheritableThreadLocal对象,并且调用了set(t)方法后,InheritableThreadLocal就会帮我们把当前线程所对应的Thread类中的inheritableThreadLocals这个属性初始化并进行赋值,而当我们new Thread() 对象时 Thread的构造函数会判断父线程的inheritableThreadLocals有没有初始化,如果初始化了 就会把父线程中的inheritableThreadLocals中的值复制到一个新创建的 ThreadLocalMap中然后赋值给子线程中的inheritableThreadLocals属性,那么子线程中的inheritableThreadLocals就有了父线程中inheritableThreadLocals的值了

注意

对于
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
这个方法我在上面一直说的是复制父ThreadLocalMap的值然后创建一个新的ThreadLocalMap对象然后这个新创建的ThreadLocalMap就包含了父ThreadLocalMap的所有值
我这里说复制只是为了方便你们理解,至于底层实现到底是不是复制的你们感兴趣的研究下

在这里插入图片描述

肯定有讲错了,或者讲的不好的地方,欢迎各路大神指正

InheritableThreadLocal是Java中的一个类,它是ThreadLocal的子类。它的原理是在父线程和子线程之间传递数据。当一个线程创建子线程时,子线程会继承父线程中的InheritableThreadLocal变量的值。这样,通过InheritableThreadLocal,父线程中设置的值可以在子线程中获取到。 InheritableThreadLocal类中有一个protected方法childValue,该方法用于定义子线程在继承父线程的InheritableThreadLocal变量时如何处理值。默认情况下,childValue方法返回父线程中的值。 此外,InheritableThreadLocal类还包含了一些其他方法,比如getMap方法用于获取当前线程的InheritableThreadLocalMap对象,createMap方法用于创建当前线程的InheritableThreadLocalMap对象并初始化第一个值。通过这些方法,InheritableThreadLocal能够实现父子线程之间的值传递。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [InheritableThreadLocal 实现原理](https://blog.csdn.net/mccand1234/article/details/116357251)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [undefined](undefined)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值