阅读这篇博客前,请确保你对ThreadLocal的使用场景和实现原理有一定了解。若对ThreadLocal的认知处于白纸阶段,可以先去看下这篇博客:【Java】ThreadLocal原理与使用场景-CSDN博客。
本文主要聚焦于ThreadLocal在父子线程间传递的原理。
Code:
package test;
public class InheritableThreadLocalTest {
public static void main(String[] args) {
User u1 = new User(1,"王祖贤");
ThreadLocal threadLocal = new ThreadLocal<>();
threadLocal.set(u1);
User u2 = new User(2,"邱淑贞");
InheritableThreadLocal inheritableThreadLocal = new InheritableThreadLocal<>();
inheritableThreadLocal.set(u2);
Thread subThread = new Thread(() -> {
User user1 = threadLocal.get();
System.out.println("获取父线程的ThreadLocal:" + user1);
User user2 = inheritableThreadLocal.get();
System.out.println("获取父线程的InheritableThreadLocal:" + user2);
}, "SubThread");
subThread.start();
}
}
代码解读:
1、main线程(父线程)创建了一个User对象,将其保存到了ThreadLocal中。
2、main线程(父线程)又创建了一个User对象,将其保存到了InheritableThreadLocal中。
3、创建了一个名为SubThread的子线程,其任务逻辑就是去分别获取保存到ThreadLocal中的值,以及保存到InheritableThreadLocal中的值。
运行结果:
我们可以看到,子线程尝试获取ThreadLocal保存的值时,得到的是null;而尝试获取InheritableThreadLocal保存的值时,能正确的得到父线程保存的值。想要知道原因,必然需要清楚InheritableThreadLocal类的实现!
public class InheritableThreadLocal extends ThreadLocal {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
//返回的是Thread中的inheritableThreadLocals字段
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
//初始化的是Thread中的inheritableThreadLocals字段
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
通过源码我们可以看到,InheritableThreadLocal继承自ThreadLocal,只是重写了childValue、getMap、createMap三个方法。
(1)getMap():
public void set(T value) {
Thread t = Thread.currentThread();
//getMap方法获取的是inheritableThreadLocals字段
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//createMap方法初始化的是inheritableThreadLocals字段
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public class Thread implements Runnable {
//成员变量,每个线程都有
ThreadLocal.ThreadLocalMap threadLocals = null;
//成员变量,每个线程都有
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
重写了getMap方法后,使用set、get方法操作的不再是threadLocals字段,而是inheritableThreadLocals字段。
(2)createMap():
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
//getMap方法获取的是inheritableThreadLocals字段
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//createMap方法初始化的是inheritableThreadLocals字段
createMap(t, value);
return value;
}
与上述一样,在线程第一次调用set、get方法时,初始化的是inheritableThreadLocals字段。
我们再来看看Thread是如何初始化的:
//默认构造
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();
//....
//inheritThreadLocals:boolean类型(总是为true)
//parent.inheritableThreadLocals:ThreadLocalMap
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
//如果父线程的inheritableThreadLocals不为null
//则设置子线程的inheritableThreadLocals
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
重点是inheritThreadLocals字段的初始化,所以init函数省去了很多无关代码。
(3)createInheritedMap(ThreadLocalMap parentMap):
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
这段代码的主要作用是:将父线程ThreadLocalMap中的键值对复制到新的ThreadLocalMap 实例中,同时通过调用childValue方法(重写)更新值。
1、子线程在初始化时,会查看父线程的inheritableThreadLocals字段是否为null,如果不为null,则会根据父线程的inheritableThreadLocals字段去初始化子线程的inheritableThreadLocals字段。
2、InheritableThreadLocal类重写了getMap、createMap两个方法,在使用InheritableThreadLocal对象调用set、get等方法时,操作的是线程的inheritableThreadLocals字段。