前面使用一篇博客介绍了ThreadLocal的理解与应用,不过ThreadLocal只能实现在同一个线程中进行值传递,不能实现父子线程之间的值传递,如果想要实现父子线程之间的值传递,就需要使用今天的主角——InheritableThreadLocal。
先用代码演示一下:
- ThreadLocal
public class InheritableThreadLocalTest {
private static ThreadLocal<String> tl = new ThreadLocal<>();
public static void main(String[] args) {
tl.set("main thread value.");
// 开启一个子线程
new Thread(() -> {
System.out.println("从父线程获取的值:" + tl.get());
}).start();
}
}
输出结果:
从父线程获取的值:null
- InheritableThreadLocal
public class InheritableThreadLocalTest {
private static ThreadLocal<String> tl = new InheritableThreadLocal<>();
public static void main(String[] args) {
tl.set("main thread value.");
// 开启一个子线程
new Thread(() -> {
System.out.println("从父线程获取的值:" + tl.get());
}).start();
}
}
输出结果:
从父线程获取的值:main thread value.
可以看到使用InheritableThreadLocal确实成功实现了父子线程之间的值传递,这是为什么呢?
下面解析一下InheritableThreadLocal的源码:
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
* <p>
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* Create the map associated with a ThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
InheritableThreadLocal的源码比较简洁,可以看到InheritableThreadLocal是继承了ThreadLocal的,所以它和ThreadLocal有很多共性。另外它重写了ThreadLocal中的三个方法,特别是getMap和createMap,用到的是线程的inheritableThreadLocals 成员变量。
和ThreadLocal相比,InheritableThreadLocal类set的时候赋值给的是t.inheritableThreadLocals,所以取map的时候也是从t.inheritableThreadLocals取的,唯一的区别就是map存取的地方不一样,那它是怎么实现父子线程之间的值传递的呢?
其实主要就是靠Thread类中的init函数实现的
// 构造函数
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
// 省略无关代码
...
Thread parent = currentThread();
...
// 省略无关代码
...
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
...
}
上面的init函数,我只例举了部分代码,不过通过核心代码还是可以看出来,在创建子线程的时候,会判断父线程中的inheritableThreadLocals 是否为空(inheritThreadLocals参数的值传递为true),在上面的代码中,使用
private static ThreadLocal<String> tl = new InheritableThreadLocal<>();
tl.set("main thread value.");
对父线程中的inheritableThreadLocals赋值了,此时父线程的inheritableThreadLocals肯定不为空,此时会调用
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
对子线程的inheritableThreadLocals 进行赋值操作,看一下createInheritedMap方法源码
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
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) {
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++;
}
}
}
}
述代码很简单,就是拿到父线程的ThreadLocalMap,然后进行复制(浅拷贝,引用复制),这样子线程的inheritableThreadLocals就有了对应的ThreadLocalMap,这样通过ThreadLocalMap就可以取到和父线程同样的值了。