阅读
Thread.Class 代码片段
1
2
3
4
5
6
7
ThreadLocal.ThreadLocalMap threadLocals = null;
private void exit() {
...
threadLocals = null;
...
}
每个线程被初始化之后,都会存在一个默认为 null 的 ThreadLocalMap 属性。这个属性会在线程结束前,被系统清除释放占用的内存。
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
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();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocal
也就是,数据 T 的存取是依赖当前线程引用进行的。
ThreadLocal.ThreadLocalMap 代码片段
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
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private Entry[] table;
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
ThreadLocalMap 虽然命名为 Map,但实际数据存储在 Entry 数组之中。Entry 又是一个特殊的静态内部类,继承了 弱引用 的 ThreadLocal<?> 类。
到这里 ThreadLocal.Class 的基本逻辑就差不多了。剩下的就是 ThreadLocalMap 中 Entry[] 的数据存取移除以及发生碰撞后如何寻找位置。因为多个线程计算得出的数组下标 i 存在碰撞的情况,所以多个线程存取数据效率并不是很高。
思考
完整场景
- 初始化 ThreadLocal
threadLocal = new ThreadLocal () - 存数据:threadLocal.set()
- 取数据:threadLocal.get()
- 线程运行时,GC 回收 T 占用内存
- 线程结束时,GC 回收 T 占用内存
内存泄漏案例
1
2
3
4
5
6
7
8
9
10
ThreadLocal<My50MB> threadLocal = new ThreadLocal<>();
threadLocal.set(new My50MB());
System.gc();
// 正常回收
threadLocal.set(new My50MB());
threadLocal = null;
System.gc();
// 无法正常回收
解决方式
- 主动调用 remove 方法
- 调用 replaceStaleEntry 方法回收键为 null 的 Entry 对象的值
总结
- ThreadLocal 并不能线程间共享数据
- ThreadLocalMap 每个线程的单独数据实例
- ThreadLocalMap 只能被持有它的线程访问