InheritableThreadLocal解决ThreadLocal子线程不可继承父线程变量的问题

当我用Mybatis-plus自动填充创建人员,更新人员字段的时候,发现数据库中又很多字段都没有记录这两个字段,通过分析发现是因为这些更新或者创建操作是在异步的情况下去执行的,由于父线程创建的子线程无法获取父线程的ThreadLocal变量,导致无法正确的获取这两个字段。

以下代码说明使用ThreadLocal 子线程无法访问父线程的线程变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestThreadLocal {
public static ThreadLocal<String> username = new ThreadLocal<>();

public static void main(String[] args) {
username.set("beixian");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程获取:"+username.get());
}
});
thread.start();
System.out.println("main线程获取"+username.get());
}
}

TestThreadLocal.main执行结果

原因:子线程ThreadLocal get()获取的的是子线程的threadLocals里的值,而父线程set()的是父线程的threadLocals里的值,而子线程创建的时候没有继承threadLocals里的值。

以下代码说明使用InheritableThreadLocal子线程可以访问父线程的线程变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TestInheritableThreadLocal {
public static InheritableThreadLocal<String> username = new InheritableThreadLocal<>();

public static void main(String[] args) {
username.set("beixian");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("子线程获取:"+username.get());
}
});
thread.start();
System.out.println("main线程获取"+username.get());
}
}

TestInheritableThreadLocal.main执行结果

解决方案:InheritableThreadLocal可以帮助我们解决这个问题,子线程在初始化的时候会通过父线程的inheritThreadLocals变量初始化子线程的inheritThreadLocals变量,这样子线程拥有了父线程的线程变量并可访问。

Thread init()源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//Thread的构造方法
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}

private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
//这里设置了inheritThreadLocals为true
init(g, target, name, stackSize, null, true);
}

private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
/**省略部分代码**/
// inheritThreadLocals为true 且 父线程的inheritThreadLocals不为空
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/**省略部分代码**/
}

static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
InheritableThreadLocal源码

InheritableThreadLocal继承于ThreadLocal,只是重写了childValue(),getMap(),createMap()方法,重写的原因是因为要获取和操作的不在是Thread.threadLocals这个变量,而是inheritableThreadLocals这个变量

get()、set()等方法同ThreadLocal,可以参考【Threadlocal的简单理解和部分源码分析】这篇文章

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

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子线程不可继承父线程变量的问题
https://cason.work/2022/06/17/InheritableThreadLocal解决ThreadLocal子线程不可继承父线程变量的问题/
作者
Cason Mo
发布于
2022年6月17日
许可协议