ThreadLocal
使用场景:
场景1. ThreadLocal用作保存每个线程独享的对象,为每个线程都创建一个副本,修改时只是对自己副本的操作,确保线程安全。典型的需要使用的类就是 SimpleDateFormat。
场景2.ThreadLocal每个线程需要独立保存信息,以便提供其他方法更方便的获取。避免了参数的传递,类似与全局变量的概念。
Thread、ThreadLocal、ThreadLocalMap之间关系
我们先从ThreadLocal#set方法开始:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
首先我们可以发现根据这行代码 **map.set(this, value);**可以得出结论是ThreadLocalMap存放的Key是ThreadLocal,value是我们存放的值。
ThreadLocalMap的Entry table:
Key | value |
---|---|
ThreadLocal | value |
ThreadLocal | value |
ThreadLocal | value |
那么ThreadLocal是如何创建的呢:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
Thread存在一个变量threadLocals
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
所以在ThreadLocal的set方法中如果第一次ThreadLocalMap不存在会创建ThreadLocalMap并且赋值给当前线程的Thread类的threadLocals变量。
综上所述:
所以每个线程都有与之对应的ThreadLocalMap,在ThreadLocal第一次使用set的时候被创建出来。每个ThreadLocalMap中存放了一个Entry的数组,其中Entry对象的Key为ThreadLocal ,value为所保存的值。
ThreadLocal内存泄漏分析
什么是内存泄漏?
当一个对象不再被使用但是却无法回收其占用的内存,我们称之为内存泄漏。
根据上面的分析我们画出了ThreadLocal的内存引用图。关于 Java中的四种引用类型
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
这里的Entry为啥是弱引用(WeakReference)?
如果不是弱引用当ThreadLocal为null 的时候依然存在一条引用:
Thread -> ThreadLocalMap -> Entry key -> ThreadLocal,这时候Key依然是无法被垃圾回收所回收掉的。当这个引用为弱引用ThreadLocal的内存就会被垃圾回收回收掉,所以Key才要设计成弱引用。
value泄漏:
虽然上面的解决了Key Threadlocal的泄漏问题,但是Entry中的存在(null,value)这种的数据,ThreadLocal 每次set.get的时候都会删除这样的entry对象。JDK同样也考虑到了这个问题,在执行ThreadLocal的set、remove、rehash等方法时,它都会扫描key为null的Entry,如果发现某个Entry的key为null,这样value就可以被回收了。
但是假设threadLocal已经不使用了 但是线程一直存活,这些方法没有被调用,那还是会泄漏。
如何避免:
调用 ThreadLocal 的 remove 方法
拉勾教育版权所有:https://kaiwu.lagou.com/course/courseInfo.htm?courseId=16调用 ThreadLocal 的 remove 方法。