ThreadLocal顾名思义,线程本地,即各个线程互不干扰的空间,每个线程只能看到当前线程放入的对象。
如下以泛型类型为String的类型为例,分析其内部结构图。
ThreadLocal<String> tl = new ThreadLocal<>();
tl.set("hellowThread");
有上图可以看出,放入ThreadLocal容器中的元素其实是放到该线程的成员变量threadlocals中的,其本质是一个Map,key存的是当前ThreadLocal对象,value存的是真实的对象。
public class ThreadLocal<T> {
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
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;
}
}
从上述set()方法和get()方法中都可以看出,都是先获取到Thread对象中的ThreadLocalMap对象,然后对map进行操作的。如此每个线程只能拿到当前线程的成员变量map,因此肯定可以保证线程间互不干扰。
弱引用:只要遭遇gc,就回收该引用指向的区域。
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
ThreadLocalMap中的Entry是继承自 WeakReference,也就是说Entry对象中指向ThreadLocal对象的引用是一个弱引用。
这样做的目的是当tl被回收后,使得ThreadLocal对象自动被gc回收,避免内存泄漏。
假设Entry中的key指向ThreadLocal对象的引用是一个强引用,当前线程执行完后会回收其局部变量tl(例如tl = null),此时已经无法访问到该ThreadLocal对象了,但是由于强引用的存在,jvm觉得该ThreadLocal对象不是一个垃圾,因此不会回收,如此会引起内存泄漏问题。使用弱引用时就可以避免该问题,只要强引用tl一断,下次gc就可以回收ThreadLocal这部分内存。
此时还是存在内存泄漏问题,还是接着上述结构,ThreadLocal对象已经被回收了,此时的key为null,因此对于threadlocals中该key所对应的那个value永远就访问不到了,但是由于value强引用的存在,“hellowThread”所占的这部分内存还是得不到回收,因此还是会产生内存泄漏问题。对于该问题的解决方法为用完Threadlocal之后,应该执行其的remove()方法。