ThreadLocal
# ThreadLocal是什么?
提供了一种线程本地变量的机制,每个线程都有自己独立副本,互补干扰;意味着每个线程可以独立操作自己的变量副本,不会影响其它线程副本;
# 案例
private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
threadLocal.set("value1");
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
threadLocal.remove(); // 可以手动调用 remove() 方法来清除当前线程的变量副本
});
Thread thread2 = new Thread(() -> {
threadLocal.set("value2");
System.out.println(Thread.currentThread().getName() + ": " + threadLocal.get());
threadLocal.remove();
});
thread1.start();
thread2.start();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
访问threadLocal这个变量的每个线程都有这个变量的副本,可以使用 get()和 set() 方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题;
# 源码
Thread
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
1
2
3
2
3
ThreadLocal
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;
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
ThreadLocal是ThreadLocalMap的封装,每个Thread都有一个ThreadLocalMap,可以存储以ThreadLocal为 key ,Object 对象为 value 的键值对
ThreadLocalMap
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# 内存泄漏
ThreadLocalMap的key为弱引用,如果ThreadLocal没有被外部强引用,在垃圾回收时,key会被清理掉,而val不会被清理,所以产生内存泄露,使用完ThreadLocal后,手动调用remove方法。
为什么key为弱引用?
便于下次垃圾回收清理掉,假如用户A执行方法时产生一份ThreadLocalA,很长时间都用不到,占用系统资源
为什么val为强引用?
ThreadLocal在运行期间都是强引用,如果val是弱引用,val就是只被Entry这个弱引用引用,发生gc时,threadLocal不会被回收,而val会被回收,导致ThreadLocal获得的val值为null,丧失了存储本地变量的意义