ThreadLocal是什么?
ThreadLocal就是指线程局部变量,就是指多个线程并发运行的时候,使用ThreadLocal装饰的变量在每个线程里都是单独使用的。
好处
由于每个线程拥有了自己的变量,所以消除了多线程情况下的竞争关系。
实现原理
要理解ThreadLocal的实现原理,那还是得从源码出发
public void set(T value) {
Thread t = Thread.currentThread(); // 获取当前线程
ThreadLocalMap map = getMap(t); // 获取一个map
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
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();
}
由上面的代码可知,设置和获取值得时候都会获取到一个ThreadLocalMap,并且用ThreadLocal作为key,真正需要使用的值作为value
ThreadLocal.ThreadLocalMap threadLocals = null; // 当前线程的map
接下来还需要关注ThreadLocalMap对象的存储方式
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
从Entry可以很明细的知道,在map中的key是一个弱引用,当key在没有强引用的时候,gc的时候key就会被回收掉,也就是当ThreadLocal变量设置为null的时候就会被回收
为什么key设置为弱引用?
首先假设key不是弱引用,而是强引用,当ThreadLocal变量设置为null的时候,由于map中还存在强引用,那么ThreadLocal变量在线程没结束就永远不会被gc回收掉。那么这样就会出现内存泄漏的问题。
所以为了避免ThreadLocal设置为null的时候不会因为map引用而导致内存泄漏的问题,所以key就必须用弱引用。
ThreadLocal为什么会出现内存泄漏?
从源码分析可知道,value值是强引用的,当ThreadLocal变量设置为null的时候,key被回收了,但value还没被回收,而且由于key为null而导致value永远不可达,虽然ThreadLocalMap中的set(),get(),remove()这些方法引用的时候,会将key为null,value不为null的清除掉。但假设ThreadLocal变量设置为null的时候,长时间没调用过以上的3个方法,并且线程长时间存在,这时候就会导致value内存泄漏了。
怎么避免ThreadLocal的内存泄漏?
每次使用完ThreadLocal之后都调用一下remove()方法,清除数据,这样就可以避免内存泄漏问题了。