本文基本jdk1.8。
日常开发中可以用ThreadLocal保存本线程的专属数据。但是要进行并行操作时,如果将父线程数据传递给后续的子线程呢?
答案是InheritableThreadLocal。
1.InheritableThreadLocal
InheritableThreadLocal源码比较简单,继承自ThreadLocal,不了解的可以参考之前ThreadLocal的解析。
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
// 这里也是调整为获取 inheritableThreadLocals而不是threadLocals
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
// 覆盖了ThreadLocal的createMap方法,区别在于ThreadLocal赋值给了t.threadLocals
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
整个类主要采用inheritableThreadLocals替代 threadLocals ,但这里看着一定一头雾水,凭啥换了参数就实现了线程参数传递。
那具体的传递过程在哪里呢?
答案在Thread里。
2. Thread
实际化一个Thread
Thread thread = new Thread();
进入构造方法,一路递进到init方法。
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
...
// InheritableThreadLocal 时存在这个参数
if (parent.inheritableThreadLocals != null)
// 子线程执行赋值父线程的数据
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.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) {
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
// 过滤那些失效的ThreadLocal
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
// 重新计算索引
int h = key.threadLocalHashCode & (len - 1);
// TheadLocal采用拉链存储,hash相同则后移至下一个null的位置
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
3.遗留问题
InheritableThreadLocal 虽然解决了父子线程参数传递问题,但是实际开发时,线程都是从线程池中获取的,这就导致了并不一定每次线程都要进行初始化操作,线程的数据存在缓存问题。
当然解决方式也有,阿里提供的方案是transmittable