ThreadLocal
当创建一个ThreadLocal后,在某个线程中调用set()方法时,ThreadLocal会从当前线程中获得ThreadLocalMap 对象 threadLocals,threadLocals相当于一个Map,它是用数组存储对象的。每一个Entry中保存了key和value,其中key就是ThreadLocal对象,value就是我们要保存的值。其中key是一个弱引用(如果没有其他引用,例如在其他栈中的强引用或者软引用,下一次gc就会回收说引用的对象)
threadLocals根据threadLocal中的threadLocalHashCodehu值, 经过运算获得table中下标。(这里不讨论hash冲突问题)
继承关系: Entry 继承 WeakReference ,WeakReference 继承 Reference。在Reference中的有一个私有字段referent 保存传入的key。
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
// 将key在Reference的referent字段
super(k);
value = v;
}
}
当栈中的Thread Ref 引用回收后,此时key为null,而object已经不再使用,需要回收,但他被value强引用,只要线程不结束(例如使用线程池),就会存在一条从栈到object的强引用链,故object无法被gc回收。ThreadLocal的解决办法是在set,get,和remove时,移除key为null的 value对象。还有另外一个可能造成内存泄露的问题。如果ThreadLocal Ref 一直存在(例如把他声明为static变量),但保存对象obje的ct又不需要使用,那么也会造成内存泄露所以最好办法就是及时把不需要的变量移除。
下面这个例子可以帮助理解:
package com.deng;
import java.lang.ref.WeakReference;
/**
* @author :deng
* @version :1.0
* @description :
* @since :1.8
*/
public class T {
static class Task implements Runnable {
WeakReference<ThreadLocal> threadLocalWeakReference;
public void fun() {
// 这里是对ThreadLocal 的强引用
ThreadLocal<Object> threadLocal1 = new ThreadLocal<Object>();
Object o = new Object();
System.out.println(o);
threadLocal1.set(o);
// 这里是虚引用
WeakReference<ThreadLocal> threadLocalWeakReference = new WeakReference<ThreadLocal>(threadLocal1);
// 将虚引用保存
this.threadLocalWeakReference = threadLocalWeakReference;
}
public void fun2() {
System.out.println(threadLocalWeakReference.get());
}
@Override
public void run() {
fun();
// fun() 结束后,对 ThreadLocal的强引用threadLocal1就结束了,
// 那么对threadLocal对象的引用就只剩弱引用了
// gc,如果成功的话,那么就会回收threadLocal所引用的对象
System.gc();
fun2();
}
}
public static void main(String[] args) throws Exception {
Task task = new Task();
new Thread(task).start();
}
}
// 结果
// java.lang.Object@6a6daa45
// null