文章插图
如果没有重写 initialValue 时,默认会返回 null
文章插图
如果线程先前调用了set方法,在这种情况下,不会为线程调用本 initialValue 方法,而是直接用之前 set 进去的值 。
在通常情况下,每个线程最多只能调用一次 initialValue 方法,但是如果已经调用了 remove 方法之后,再调用 get 方法,则可以再次调用 initialValue 方法 。
getget 方法是先取出当前线程的 ThreadLocalMap,然后调用 map.getEntry 方法,把本 ThreadLocal 的引用作为参数传入,取出 map 中属于本 ThreadLocal 的value 。
public T get() {// 获取当前线程Thread t = Thread.currentThread();// 获取当前线程的threadLocals 成员变量ThreadLocalMap map = getMap(t);if (map != null) {// this 指的是 ThreadLocal 对象,通过 map.getEntry 来获取我们通过 set 方法设置进去的 value 值ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}
ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
set跟 get 一样,同样是先获取当前线程的引用,然后再获取当前线程的 threadLocals 成员变量,如果 threadLocals 为null,即还未初始化,就会执行 createMap 方法来进行初始化 。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 给回收掉 。
经验总结扩展阅读
- FlinkSql之TableAPI详解
- 原神机械之心任务的完成方法是什么
- 奶油冻过之后再化开还能用吗
- 百分之七十五的酒精怎么配
- 四 【单片机入门】应用层软件开发的单片机学习之路-----ESP32开发板PWM控制电机以及中断的使用
- 原神坎蒂丝命之座效果是什么
- 对亡人的哀思之情句子 人突然去世的句子
- flutter 系列之:flutter 中的幽灵offstage
- 高铁中转站怎么换乘
- 美妆蛋一定要吸水之后使用吗?