一:简述
我们知道ThreadLocal可以实现线程级别下的数据隔离,但是如果需要将当前线程的值传递给子线程,是需要自己去写逻辑实现的,会比较复杂。而InheritableThreadLocal帮助我们解决了这个问题。本篇文章就和大家一起聊聊InheritableThreadLocal。
二:InheritableThreadLocal的原理
1.数据怎么存储
我们可以看到InheritableThreadLocal继承了ThreadLocal,并且重写了ThreadLocal的getMap()以及createMap()方法。getMap()返回Thread类的inheritableThreadLocals成员变量,createMap()方法的作用是创建一个ThreadLocalMap并且赋值给当前线程的inheritableThreadLocals,所以InheritableThreadLocal的get(),set(),remove()方法是继承自ThreadLocal,实现原理和ThreadLocal一样,只不过ThreadLocal数据是存储在Thread的threadLocals成员变量中,而InheritableThreadLocal是存储在Thread的inheritableThreadLocals成员变量中。
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
// 返回当前线程的inheritableThreadLocals成员变量
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
//创建一个ThreadLocalMap 并且赋值给inheritableThreadLocals
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
注:ThreadLocal原理在我的另外一篇文章讲的很彻底,这里就不再阐述了,原文地址:TheadLocal的实现原理
2.数据是怎么传递给子线程的?
在线程的构造函数中,调动了init()方法,在init()方法中调用createInheritedMap()方法初始化子线程的inheritableThreadLocals,并且将父线程中的inheritableThreadLocals的值取出来赋值给子线程的inheritableThreadLocals。
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) {
...其他无关源码 一些赋值操作 为了篇幅这里省略了
//inheritThreadLocals默认是true
//如果需要传递并且父线程的inheritableThreadLocals不为空
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
// 那么就初始化inheritableThreadLocals并且把父线程的inheritableThreadLocals中的值传递给子线程
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize; /* Set thread ID */
tid = nextThreadID();
}
接下来我们分析createInheritedMap()方法
createInheritedMap()
createInheritedMap()方法调用了ThreadLocalMap的有参构造函数。
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
在构造函数中,遍历了父线程的inheritableThreadLocals,然后遍历父线程inheritableThreadLocals的Entry数组,重新封装成Entry,并且计算数组下标放入到子线程的inheritableThreadLocals中。这也就把数据从父线程传递给了子线程。
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
//遍历父线程inheritableThreadLocals的Entry数组
for (int j = 0; j < len; j++) {
Entry e = parentTable[j]; if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
//如果key为null 代表是无效数据 不需要传递给子线程
if (key != null) {
//取出父线程的value
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++;
}
}
}
}
总结:
InheritableThreadLocal的数据是存储在inheritableThreadLocals中的,而Thread在利用构造函数实例化的时候会将父线程的inheritableThreadLocals中的值复制一份给自己,保存在自己的inheritableThreadLocals中,这样就完成了父子线程之间的数据传递。