这么简单的InheritableThreadLocal,你还不会么?

​PS:本文需要ThreadLocal原理的前置知识!!!

ThreadLocal可以实现线程之间变量的隔离,那如果我们想让子线程读取到父线程中ThreadLocal的值呢?有需求,就有回应,实现父子线程之间通信的InheritableThreadLocal这就来了!

我们先来看一下InheritableThreadLocal的源码:

public class InheritableThreadLocal<T> extends ThreadLocal<T> {
    
    // 重写了ThreadLocal的getMap()
    // 子线程copy父线程的inheritableThreadLocals属性对象时的处理操作方法
    // InheritableThreadLocal默认给的就是原封不动【copy】父线程的值
    // 我们可以继承InheritableThreadLocal,并重写childValue(),比如改为 return parentValue + 1;
    // 那么子线程从父线程那里拿到的值,全都是 +1 后的数据
    // 可以理解为儿子继承到爸爸的财产,对财产进行处理,要么用钱赚更多钱,要么败家子
    protected T childValue(T parentValue) {
        return parentValue;
    }
    
    // 重写了ThreadLocal的getMap()
    // 使ThreadLocal的get()行为发生改变
    // 从对ThreadLocal.threadLocals进行操作,改为对ThreadLocal.inheritableThreadLocals操作
    ThreadLocalMap getMap(Thread t) {
       return t.inheritableThreadLocals;
    }
    
    // 重写了ThreadLocal的createMap()
    // 使ThreadLocal的set()行为发生改变
    // 从对ThreadLocal.threadLocals进行操作,改为对ThreadLocal.inheritableThreadLocals操作
    void createMap(Thread t, T firstValue) {
        t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
    }
}

通过inheritableThreadLocal源码,再结合几个问题,我们就可以明白inheritableThreadLocal的原理了

1、InheritableThreadLocal的值存放在哪里?

​ 可以看到,inheritableThreadLocal继承自ThreadLocal,并只重写了三个方法,其他全部复用ThreadLocal的代码,可想而知,inheritableThreadLocal的原理和ThreadLocal基本一致!

​ 首先,我们知道为了实现ThreadLocal,在Thread类中有一个ThreadLocal.ThreadLocalMap类型的threadLocals成员属性,当我们使用ThreadLocalget()、set()、remove()对目标值进行操作时,都是对threadLocals属性进行操作。

​ 那么inheritableThreadLocal的值,又是存放在哪里呢?答案是和ThreadLocal一样,也存放在Thread类中作为ThreadLocal.ThreadLocalMap类型的成员属性,不过属性名是inheritableThreadLocals。这一点结合inheritableThreadLocalThreadLocaL的源码,我们就可以轻易得出。

2、InheritableThreadLocal的值如何传递给子线程?

​ 答案就在Thread类的构造方法中:

// 创建子线程的构造方法
public Thread() {
    init(null, null, "Thread-" + nextThreadNum(), 0);
}

// init()和上面的参数对不上,因为中间层层嵌套,我这里省略掉了,最终调用的还是这个方法
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc,
                  // 该参数默认是 true
                  boolean inheritThreadLocals) {
    // ...
    // 获取正在创建当前子线程的父线程
    Thread parent = currentThread();

    // boolean inheritThreadLocals 默认是 true
    // parent.inheritableThreadLocals 在父线程使用了 inheritableThreadLocal.set()后,就不为 null了
    // 判断父线程(创建子线程的线程)的 inheritableThreadLocals 属性不为 null,
    // 即 判断子线程是否需要复制 父线程的inheritableThreadLocals
    if (inheritThreadLocals && parent.inheritableThreadLocals != null) {
        // 子线程【复制】父线程的 inheritableThreadLocals 属性,实现父子线程局部变量共享
        // 注意!!!这里的【复制】,指的是 子线程【深拷贝】了父线程的`Thread.inheritableThreadLocals`,
        // 而不是简单的把引用传递了一下,所以在创建完子线程对象后,
        // 父子线程的`Thread.inheritableThreadLocals`对象不是同一个对象了,只是当时他们内部的值是一样的,
        // 看可以理解为`==`为`false`,但是`equals()`为`true`
        this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals); 
    }
    // ..
}

// 【本质上还是创建 ThreadLocalMap,只是把父类中的可继承数据设置进去了】
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
    return new ThreadLocalMap(parentMap);
}

// 根据父线程的inheritableThreadLocals,深拷贝一份给子线程
private ThreadLocalMap(ThreadLocalMap parentMap) {
    Entry[] parentTable = parentMap.table;
    int len = parentTable.length;
    setThreshold(len);
    table = new Entry[len];

    for (int j = 0; j < len; j++) {
        Entry e = parentTable[j];
        if (e != null) {
            @SuppressWarnings("unchecked")
            ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
            if (key != null) {
                // 这里调用了 InheritableThreadLocal重写的childValue()
                // 传入父线程的值,在方法里处理父线程的值,把返回值赋给子线程
                // InheritableThreadLocal#childValue()选择了原封不动,我们可以重写自己的逻辑
                Object value = key.childValue(e.value);
                Entry c = new Entry(key, value);
                int h = key.threadLocalHashCode & (len - 1);
                while (table[h] != null)
                    h = nextIndex(h, len);
                table[h] = c;
                size++;
            }
        }
    }
}

​ 结合以上源码,我们可以得出,Thread.inheritableThreadLocal是父线程在创建子线程时,在子线程的构造方法的执行中,把父线程的inheritableThreadLocal深拷贝给子线程的inheritableThreadLocal,并可以通过重写inheritableThreadLocal#childValue的方法,对父线程的值进行处理后再赋值给子线程。

注意

需要注意的是,复制父线程共享变量的时机是在创建子线程时,如果在创建子线程后父线程再往 InheritableThreadLocal 类型的对象中设置内容,将不再对子线程可见。

即可以理解为,在父线程创建子线程对象的时候,父线程把自己Thread.inheritableThreadLocals,深拷贝给了子线程的Thread.inheritableThreadLocals,而不是简单的把引用传递了一下,所以在创建完子线程对象后,父子线程的Thread.inheritableThreadLocals对象不是同一个对象了,只是当时他们内部的值是一样的,看可以理解为==false,但是equals()true

总结

1、inheritableThreadLocalThreadLocal一样,也存放在Thread类中作为ThreadLocal.ThreadLocalMap类型的成员属性,不过属性名是inheritableThreadLocals

2、Thread.inheritableThreadLocal是父线程在创建子线程时,在子线程的构造方法的执行中,把父线程的inheritableThreadLocal深拷贝给子线程的inheritableThreadLocal,并可以通过重写inheritableThreadLocal#childValue的方法,对父线程的值进行处理后再赋值给子线程。

3、需要注意的是,复制父线程共享变量的时机是在创建子线程时,如果在创建子线程后父线程再往 InheritableThreadLocal 类型的对象中设置内容,将不再对子线程可见。

参考

参考文章: 一文让你彻底明白ThreadLocal_木子雷的博客-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值