public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)// this 指的是 ThreadLocal 对象 , value 就是想要设置进去的值map.set(this, value);elsecreateMap(t, value);}
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}
map.set(this, value);
需要注意的是,这个 map 以及 map 中的 key 和 value 都是保存在 Thread 线程中的,而不是保存在 ThreadLocal 中 。
remove原理跟 get 和 set 类似,这里就不赘述了 。
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this);}
ThreadLocal 的内存泄露内存泄漏:当某个对象不再有引用 , 但是所占用的内存不能被回收 。
下面我们来看 ThreadLocal 的静态内部类 ThreadLocalMap ,ThreadLocalMap 的 Entry 其实就是存放每一个ThreadLocal 和 value 键值对的集合 。
文章插图
文章插图
Entry 静态类的构造方法,分别执行了
super(k);
value = https://www.huyubaike.com/biancheng/v;
其中 super(k)
去父类中进行初始化,而从 Entry extends 的父类我们可以看出,WeakReference 父类是一个弱引用类,则说明了 k 值是一个弱引用的, 而 value 就是一个强引用 。强引用:任何时候都不会被回收,即使发生 GC 的时候也不会被回收(赋值就是一种强引用)由此我们可以得知,ThreadLocalMap 的每一个 Entry 都是一个对 key 的弱引用,但是每一个 Entry 都包含了一个对 value 的强引用 。而由于线程池中的线程池存活时间都比较长,那么 Entry 的 key 是可以被回收掉的 , 但是 value 无法被回收,就会发生内存泄漏 。
弱引用:对象只被弱引用关联,在下一次 GC 时会被回收 。(可以理解为只要触发一次GC,就可以扫描到并被回收掉)
JDK 的设计者也考虑到了这个不足之处,所以在经常调用的方法,比如 set, remove, rehash 会主动去扫描 key 为 null 的 Entry,并把对应的 value 设置 null , 这样 value 对象也可以被 GC 给回收掉 。
另外在阿里巴巴 Java 开发手册也明确指出,应该显式地调用 remove 方法,删除 Entry 对象 , 避免内存泄漏 。
【强制】 必须回收自定义的 ThreadLocal 变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的 ThreadLocal 变量,可能会影响到后续业务逻辑和造成内存泄漏等问题 。尽量在代码中使用 try-finally 块进行回收 。
objThreadLocal.set(someObject);try{ ...} finally { objThreadLocal.remove();}
推荐阅读
- FlinkSql之TableAPI详解
- 原神机械之心任务的完成方法是什么
- 四 【单片机入门】应用层软件开发的单片机学习之路-----ESP32开发板PWM控制电机以及中断的使用
- 原神坎蒂丝命之座效果是什么
- flutter 系列之:flutter 中的幽灵offstage
- vulnhub靶场之ICA: 1
- 云原生之旅 - 6)不能错过的一款 Kubernetes 应用编排管理神器 Kustomize
- 原神智中之宝新计划任务是什么
- 上古四大凶兽之一的梼杌有什么来历它还有别的名字吗
- 之六 2流高手速成记:从SpringBoot到SpringCloudAlibaba