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
成员属性,当我们使用ThreadLocal
的get()、set()、remove()
对目标值进行操作时,都是对threadLocals
属性进行操作。
那么inheritableThreadLocal
的值,又是存放在哪里呢?答案是和ThreadLocal
一样,也存放在Thread
类中作为ThreadLocal.ThreadLocalMap
类型的成员属性,不过属性名是inheritableThreadLocals
。这一点结合inheritableThreadLocal
和ThreadLocaL
的源码,我们就可以轻易得出。
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、inheritableThreadLocal
和ThreadLocal
一样,也存放在Thread
类中作为ThreadLocal.ThreadLocalMap
类型的成员属性,不过属性名是inheritableThreadLocals
2、Thread.inheritableThreadLocal
是父线程在创建子线程时,在子线程的构造方法的执行中,把父线程的inheritableThreadLocal
深拷贝给子线程的inheritableThreadLocal
,并可以通过重写inheritableThreadLocal#childValue
的方法,对父线程的值进行处理后再赋值给子线程。
3、需要注意的是,复制父线程共享变量的时机是在创建子线程时,如果在创建子线程后父线程再往 InheritableThreadLocal 类型的对象中设置内容,将不再对子线程可见。