0%

Java多线程之InheritableThreadLocal类

针对ThreadLocal不可继承的特点,Java还引入了InheritableThreadLocal类,顾名思义其是可以继承的

abstract.jpeg

基本实践

ThreadLocal的不可继承性指的是子线程无法继承获取到父线程中的值。而InheritableThreadLocal则通过继承ThreadLocal类实现了可继承性。测试代码如下所示

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
26
27
28
29
30
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class InheritableThreadLocalDemo {

/**
* ThreadLocal继承性测试
*/
@Test
public void test1() {
System.out.println("\n--------------------------- Test 1 ---------------------------");
ThreadLocal<String> userInfo = new ThreadLocal<>();
userInfo.set("Aaron");
new Thread( ()-> System.out.println("<子线程> userInfo: " + userInfo.get()) )
.start();
System.out.println("<父线程> userInfo: " + userInfo.get());
}

/**
* InheritableThreadLocal继承性测试
*/
@Test
public void test2() {
System.out.println("\n--------------------------- Test 2 ---------------------------");
InheritableThreadLocal<String> userInfo = new InheritableThreadLocal<>();
userInfo.set("Aaron");
new Thread( ()-> System.out.println("<子线程> userInfo: " + userInfo.get()) )
.start();
System.out.println("<父线程> userInfo: " + userInfo.get());
}

}

测试结果如下,符合预期

figure 1.jpeg

基本原理

其基本原理也很简单,该类继承了ThreadLocal并重写了下述的三个方法。众所周知,ThreadLocal是通过Thread类中ThreadLocal.ThreadLocalMap类型的threadLocals字段实现线程本地存储的。而InheritableThreadLocal则是使用Thread类中ThreadLocal.ThreadLocalMap类型的inheritableThreadLocals字段。其实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}
}

而其继承性的实现也很简单,即在子线程创建阶段通过init方法将将父线程的inheritableThreadLocals浅拷贝到子线程中,Thread实例创建过程中相关源码部分实现如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Thread implements Runnable {

public Thread(Runnable target) {
init(null, target, "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();
...
// 父线程的inheritableThreadLocals变量不为空
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
// 将父线程的inheritableThreadLocals浅拷贝到子线程
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
...
}

}

前面提到子线程虽然继承了父线程的值,但其是通过浅拷贝的方式进行的。下面通过示例代码来验证下

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class InheritableThreadLocalDemo {

@Test
public void test3() throws Exception {
System.out.println("\n--------------------------- Test 3 ---------------------------");
InheritableThreadLocal<String> userInfo = new InheritableThreadLocal<>();
userInfo.set("Aaron");
System.out.println("<父线程> userInfo #1: " + userInfo.get());
Thread thread = new Thread( ()-> {
System.out.println("<子线程> userInfo #2: " + userInfo.get());
userInfo.set("Bob");
System.out.println("<子线程> userInfo #3: " + userInfo.get());
} );
thread.start();
thread.join();
System.out.println("<父线程> userInfo #4: " + userInfo.get());
}

@Test
public void test4() throws Exception {
System.out.println("\n--------------------------- Test 4 ---------------------------");
InheritableThreadLocal<User> userInfo = new InheritableThreadLocal<>();
userInfo.set( new User("Aaron") );
System.out.println("<父线程> userInfo #1: " + userInfo.get());
Thread thread = new Thread( ()-> {
System.out.println("<子线程> userInfo #2: " + userInfo.get());
userInfo.get().setName("Bob");
System.out.println("<子线程> userInfo #3: " + userInfo.get());
} );
thread.start();
thread.join();
System.out.println("<父线程> userInfo #4: " + userInfo.get());
}

@AllArgsConstructor
@Data
private static class User {
private String name;
}

}

测试结果如下,符合预期

figure 2.jpeg

参考文献

  1. Java并发编程之美 翟陆续、薛宾田著
请我喝杯咖啡捏~

欢迎关注我的微信公众号:青灯抽丝