ThreadLocal原理
ThreadLocal
并不是一个Thread
,其内部有一个静态内部类ThreadLocalMap
,而每一个 Thread
里都有一个 ThreadLocal.ThreadLocalMap
这样的类型变量,该变量的名字叫作 threadLocals
,当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。即同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本。(每个线程有自己的变量副本,所以不会存在多线程共享问题)
ThreadLocal内存泄漏分析
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
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
弱引用,弱引用的特点是,如果这个对象只被弱引用关联,而没有任何强引用关联,那么这个对象就可以被回收,所以弱引用不会阻止 GC,所以key会被回收。但是value = v
对应的value是一个强引用,还是存在的,这就可能造成内存泄漏(因为这个时候ThreadLocalMap
会存在key
为null
但是value
不为null
的entry
项)。
如何避免内存泄漏
通过上面分析可知,ThreadLocal
内存泄漏的原因不在key
,而在于value
,所以需要在使用完ThreadLocal
时需要删除value
对象。
ThreadLocal
本身就提供了remove
方法,可以将ThreadLocalMap
对应的value
一起清除掉。
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
- 每次使用完ThreadLocal都调用remove()方法清除数据
- 将ThreadLocal变量定义成private static,这样能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉value。
private static ThreadLocal<Map<String, String>> threadLocal = new ThreadLocal<>();
另外,为什么都说每次使用完ThreadLocal
都要调用remove
呢?因为通常我们都会使用线程池,而当一个请求使用完某个线程,该线程会回到线程池被其它请求使用,就可能会出现不同的请求使用到同一个线程,及时的清除掉也可以避免不同请求之间的数据污染,