????????Java版本:8u261。
????????之前我写过对ThreadLocal源码进行分析的文章,感兴趣的话可以查看《较真儿学源码系列-ThreadLocal(逐行源码带你分析作者思路)》???????。
????????InheritableThreadLocal是ThreadLocal的子类,通过前面的分析可知,ThreadLocal只能在同一线程内进行变量的共享,而InheritableThreadLocal不仅可以在同一线程内进行变量的共享,而且可以在父子线程之间进行变量的共享。比如说在父线程a中创建了一个子线程b,那么在a线程中用InheritableThreadLocal包装的变量,在子线程b中也能获取的到。但需要注意的是:InheritableThreadLocal和ThreadLocal一样,在同级线程中依然不能共享变量的值。并且InheritableThreadLocal只能父传子,不能子传父(同时也不是任何时候都能父传子,只有在一开始初始化的时候才会进行数据传递,后面会看到这点)。
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
????????以上便是InheritableThreadLocal的全部源码,可见其只是覆写了ThreadLocal的三个方法而已。而在getMap和createMap方法中可以看到inheritableThreadLocals这个属性,那么这个属性到底是干什么的呢?其实它跟threadLocals属性一样,都是放在Thread类中的属性:
public class Thread implements Runnable {
//...
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
//...
}
????????正是因为有了inheritableThreadLocals这个属性,就可以让子线程来访问父线程中的本地变量。
????????在创建线程的时候,会调用到Thread类的init方法:
/**
* Thread:
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//...
Thread parent = currentThread();
//...
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
/*
将父线程中inheritableThreadLocals的数据初始化到一个新的ThreadLocalMap中,
并赋值给子线程的inheritableThreadLocals
*/
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
//...
}
????????这里省略了其他不相干逻辑,只需要来看一下inheritableThreadLocals的初始化过程,进一步跟踪第17行代码处:
/**
* ThreadLocal:
*/
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
//parentMap是父线程中的数据
return new ThreadLocalMap(parentMap);
}
/**
* 将父线程中的inheritableThreadLocals复制到一个新的ThreadLocalMap中
* 个人认为这里直接将parentMap返回回去应该也是可以的,但这里重新构建一个
* ThreadLocalMap感觉是为了做一遍清理工作,将Entry为null的哈希槽清理掉
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
//设置子线程的threshold
setThreshold(len);
//初始化子线程的table(因为是在子线程创建的时候调用到这里的,所以不需要判断是否已经初始化,这里一定是未初始化的)
table = new Entry[len];
//遍历父线程中的table
for (int j = 0; j < len; j++) {
//获取其中的Entry
Entry e = parentTable[j];
if (e != null) {
//获取当前槽中的Entry中保存的ThreadLocal
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
//这里会调用InheritableThreadLocal覆写的childValue方法,也就是返回e.value本身
Object value = key.childValue(e.value);
//构建一个新的Entry
Entry c = new Entry(key, value);
//获取哈希槽的位置
int h = key.threadLocalHashCode & (len - 1);
//通过线性探测的方式来找到要插入的位置
while (table[h] != null)
h = nextIndex(h, len);
//插入数据
table[h] = c;
//计数+1
size++;
}
}
}
}
/**
* InheritableThreadLocal:
* 第32行代码处:
*/
protected T childValue(T parentValue) {
return parentValue;
}
????????完整的流程如下:
- 首先父线程调用set或get方法时,会调用InheritableThreadLocal覆写的getMap方法返回inheritableThreadLocals,但因为其没有初始化,所以会调用覆写的createMap方法来创建ThreadLocalMap,并赋值给inheritableThreadLocals。这样父线程的inheritableThreadLocals属性就不为null了;
- 接着父线程会调用set方法往父线程的inheritableThreadLocals属性中的table数组中进行赋值;
- 然后创建子线程的时候,会调用Thread类的init方法中的ThreadLocal.createInheritedMap方法。将父线程inheritableThreadLocals属性中的数据初始化到一个新的ThreadLocalMap中,并同时赋值给子线程的inheritableThreadLocals。这样子线程的inheritableThreadLocals属性中就有了父线程中的数据;
- 最后子线程在调用get方法的时候就能拿到父线程中的数据了。但是需要注意的是:子线程执行完毕后,父线程此时调用get方法拿到的还是之前父线程中的inheritableThreadLocals,并不是子线程会往其中更改过的ThreadLocalMap。也就是说子线程的数据不会传递给父线程,子线程只有在一开始初始化的时候才会同步父线程中的数据。
原创不易,未得准许,请勿转载,翻版必究
|