• Thread类里面有一个threadLocals成员变量

ThreadLocal_1

  • ThreadLocal类下面有一个静态内部类ThreadLocalMap

ThreadLocal_2

  • ThreadLocal类下有get,set,remove方法

ThreadLocal_3

ThreadLocal_4

ThreadLocal_5

ThreadLocal_6

ThreadLocal_7

  • 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public class Solution {

// 初始化方式一,重写
private static ThreadLocal<String> threadLocal_1 = new ThreadLocal<>() {
@Override
public String initialValue() {
return "initialVal_1";
}
}

// 初始化方式二
private static ThreadLocal<String> threadLocal_2 = ThreadLocal.withInitial(() -> "initialVal_2");

public static void method() {
Thread td_1 = new Thread(() -> {
threadLocal_1.set("td_1_threadLocal_1");
threadLocal_2.set("td_1_threadLocal_2");

// get返回线程局部变量的副本值
System.out.ptintln("td_1_threadLocal_1: " + threadLocal_1.get());
System.out.println("td_1_threadLocal_2: " + threadLocal_2.get());

threadLocal_1.remove();
threadLocal_2.remove();

System.out.println("after remove, threadLocal_1: " + threadLocal_1.get());
System.out.println("after remove, threadLocal_2: " + threadLocal_2.get());
});

Thread td_2 = new Thread(() -> {
threadLocal_1.set("td_2_threadLocal_1");
threadLocal_2.set("td_2_threadLocal_2");

try {
Thread.sleep(2000);
} catch(InterruptedException e) {
e.printStackTrace();
}

System.out.println("after sleep 2000ms, td_2_threadLocal_1: " + threadLocal_1.get());
System.out.println("after sleep 2000ms, td_2_threadLocal_2: " + threadLocal_2.get());

threadLocal_1.remove();
threadLocal_2.remove();
System.out.println("after remove, threadLocal_1: " + threadLocal_1.get());
System.out.println("after remove, threadLocal_2: " + threadLocal_2.get());
});

td_1.start();
td_2.start();
}
}


/*
* thread1_threadLocal1 : thread1_threadLocal1
* thread1_threadLocal2 : thread1_threadLocal2
* after remove : threadLocal1 : null
* after remove : threadLocal2 : null
* after sleep 2000ms, thread2_threadLocal1 : thread2_threadLocal1
* after sleep 2000ms, thread2_threadLocal2 : thread2_threadLocal2
* after remove : threadLocal1 : null
* after remove : threadLocal2 : null
* */

/*
* 每个thread下存有一个 ThreadLocal.ThreadLocalMap threadLocals, threadLocals中存放着threadLocal1、threadLocal2、threadLocal3、....,每一个threadLocal以键值对形式存在
* 线程局部变量,各个线程之间独立
* 需手动remove,否则若线程一直存在时,将导致内存泄漏
* */

/*
* 对于初始化值,第一次调用get方法访问该值时调用初始化方法,但若在get之前调用了set方法,则不会再调用初始化方法。
* 正常情况下初始化方法只会被调用一次,但如果remove了,则下次get时还会再调用一次初始化方法
* https://www.jianshu.com/p/73a1c128c5d9
* */


  • ThreadLocal的静态内部类ThreadLocalMap为每一个Thread都维护了一个数组table。ThreadLocal通过数组下标,将不同value存储到对应的位置。
  • 一个Thread只持有一个ThreadLocalMap,所以ABCD对应同一个ThreadLocalMap,ABCD存储在数组的不同位置。
1
2
3
4
5
6
7
8
9
10
11
public class ApplicationDemo {
// 每一个线程都分别存在ABCD四个线程局部变量
public static ThreadLocal<String> localVarA = new ThreadLocal<>();
public static ThreadLocal<String> localVarB = new ThreadLocal<>();
public static ThreadLocal<String> localVarC = new ThreadLocal<>();
public static ThreadLocal<String> localVarD = new ThreadLocal<>();

public static void main(String[] args){

}
}
  • 总结如下:
    • 对于某一ThreadLocal来讲,它的索引值i是确定的,在不同线程访问时访问的是不同table数组的同一位置即都是table[i],只不过这个不同线程之间的table是独立的。
    • 对于同一线程的不同ThreadLocal来讲,这些ThreadLocal实例共享一个table数组,然后每个ThreadLocal实例在table中的索引值i是不同的。
  • remove()方法判断某一线程的某个线程局部变量是否为null,不为null则删除该线程局部变量。(每个线程的线程局部变量存放在自己的本地内存变量ThreadLocals中,若该线程不消亡,则线程局部变量就会一直存在,可能导致内存溢出,因此用毕后需及时remove掉)
  • ThreadLocal类是不支持继承的,即父线程设置的值,子线程无法获取。想要支持继承的需要使用InheritableThread类。

摘自/参考链接1

摘自/参考链接2