spb 2020-05-26
ThreadLocal 是一个存储了线程独有变量的类。
ThreadLocal 主要是通过自身的一个静态内部类 ThreadLocalMap,对当前线程的变量进行存储的。
首先我们来看一下这个静态内部类:
那看完了他们的结构关系,我们来看看它是如何 存值 的:ThreadLoacal 中的 set 方法
public void set(T value) { Thread t = Thread.currentThread(); // 获取当前线程 ThreadLocalMap map = getMap(t); // 获取当前线程独有的 ThreadLocalMap if (map != null) map.set(this, value); // 当前线程对象作为 key,存放键值对 else createMap(t, value); }
分析:注意看,里面调用了一个方法: getMap(Thread t),看到这个方法,我们就可以联想到刚刚提到的 ThreadLocalMap,点进去看一下!
ThreadLocalMap getMap(Thread t) { return t.threadLocals; }
分析:原来它是返回了传入参数的线程内部的 threadLocals 这个属性,点过去看一下!
看到这里,我们就有些明白了。它实际上,是将当前线程对象获取出来,然后把当前线程对象作为key,以 key:value 的形式存入到它的静态内部类中的 Entry 数组中去了。
一开始看整体结构的时候,我们就发现了,Entry 数组它继承了 弱引用类,那原因是什么呢?
举个例子:首先在 main 线程中定义一个方法,里面创建一个 ThreadLocal对象,进行存取值
public void func1() { ThreadLocal t = new ThreadLocal<Integer>(); t.set(1); t.get(); }
然后我们观察一下:
分析:我们观察可以看见,main 线程的栈中有个栈帧,里面的 ThreadLocal 对象 t 指向了它内部的 ThreadLocal,而 ThreadLocalMap 的 Entry 数组中的 key,也是当前线程对象,也指向了 ThreadLocal。
做一个假设,Entry不使用弱引用:如果方法执行结束,栈帧销毁,那么强引用就没了。但是静态内部类中的成员Entry的引用还在,如果我们不声明为弱引用,那么默认的强引用,将会导致该 ThreadLocal 对象一直不能被回收,将会导致内存泄漏,而弱引用就不同了,一旦栈帧中的对象销毁,该对象只要被GC线程发现,就会被回收了!