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
public class ThreadLocalTest implements Runnable {

private final ThreadLocal<Integer> local = ThreadLocal.withInitial(() -> 0);

private final ThreadLocal<String> stringLocal = new ThreadLocal<>();


public Integer add() {

local.set(local.get() + 1);

return local.get() + 1;

}

public String setStringLocal(String threadName) {

try {

stringLocal.set(threadName);

return stringLocal.get();

} finally {
stringLocal.remove();
}
}

@Override
public void run() {

for (int i = 0; i < 3; i++) {
System.out.println(add() + " --- " + setStringLocal(Thread.currentThread().getName()));
}

}

public static void main(String[] args) {

ThreadLocalTest threadLocalTest = new ThreadLocalTest();

new Thread(threadLocalTest).start();

new Thread(threadLocalTest).start();

}
}

打印展示

ThreadLocal的使用

内部数据结构

1
一个线程类中有一个ThreadLocalMap,ThreadLocalMap中可以有多个Entry,每个Entry都存储了一个ThreadLocal本地变量

set方法源码解析

set方法源码
getMap
创建ThreadLocalMap对象

1
2
3
1.调用时需要传递一个参数
2.进入set方法后,首先获取一个线程对象,使用getMap方法获取当中的ThreadLocalMap对象
3.判断ThreadLocalMap对象是否为空,不为空就用ThreadLocal对象作为Key,值作为Value存储到ThreadLocalMap对象中;为空则直接用当前线程和传递的值创建ThreadLocalMap对象

get方法源码

get方法源码
setInitialValue源码
initialValue源码

1
2
1.进入get方法后,首先同set方法一样获取一个线程对象,使用getMap方法获取当中的ThreadLocalMap对象
2.判断ThreadLocalMap对象是否为空,不为空就用ThreadLocal对象作为键获取Entry对象,然后再判断Entry对象是否为空,不为空就直接获取value值,将其强转成对应数据类型的数据返回;若ThreadLocalMap对象为空,则调用setInitialValue方法返回,setInitialValue方法中首先调用了initialValue方法,initialValue方法中的返回值为null,再回到setInitialValue方法中往下走就同set方法一致了,但走到最后一步会将null返回

可能会导致的内存泄漏

问题原因

1
2
3
1.在ThreadLocal中保存值时,会放入Entry对象
2.Entry是基于弱引用的,若创建的ThreadLocal的线程终止,Entry对象将会被回收
3.但若是创建的ThreadLocal的线程一直持续运行,那当中的value就可能无法回收

解决方法

1
合理使用ThreadLocal对象,并及时的对其使用remove方法进行资源释放