1、threadLocal图解
java.lang.ThreadLocal类实现了线程的本地存储。
ThreadLocal的内部实现:
ThreadLocal的内部实现包括一个类似HashMap的对象,这里称之ThreadLocalMap。
ThreadLocalMap的key会持有对ThreadLocal实例的弱引用(Weak Reference),value会引用具体存储的对象实例。
【强引用】:
1、threalLocal对象指向threalLocalMap中的key .
2、线程对象 指向 堆中的 threalLocalMap。
【弱引用】:
1、threalLocalMap 的key为 threadLocal对象,即threadLocal对象 指向key为弱引用。
部分源码如下:
// ThreadLocal类中的get()方法:
// 代码逻辑:
// 1、通过当前线程对象,获取线程对象中的成员变量threadLocals 即threadLocalMap。
// 2、拿到threadLocalMap后,根据当前threadLocal对象作为key,获取value值。
// 3、如果threadLocalMap为空,进行初始化
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();
}
// ThreadLocal类中的setInitialValue()方法: 设置ThreadLocalMap的初始化值。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
// ThreadLocal类中的initialValue()方法:ThreadLocalMap初始value为null
protected T initialValue() {
return null;
}
// ThreadLocal类中的createMap方法: 初始化ThreadLocalMap,即线程的threadLocals对象初始化。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
// ThreadLocalMap类的构造方法:存储用的是Entry数组。Entry的key为ThreadLocal对象。
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
// ThreadLocal类中的getMap方法:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// ThreadLocal类中的remove方法:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
// ThreadLocalMap类中的remove方法:
private void remove(ThreadLocal<?> key) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
if (e.get() == key) {
e.clear();
expungeStaleEntry(i);
return;
}
}
}
// Thread类中的成员变量threadLocal即: 线程中的ThreadLocalMap
ThreadLocal.ThreadLocalMap threadLocals = null;
2、ThreadLocal原理
-
Thread类中一个成员变量,ThreadLocal.ThreadLocalMap threadLocals 。
-
ThreadLocal本身不存储数据,像是一个工具类,基于ThreadLocal去操作ThreadLocalMap。
-
ThreadLocalMap本身就是基于Entrty[]实现的,因为一个线程被可以绑定多个ThreadLocalMap,这样一来,可能需要存储多个数据,所以采用Entrty[]的形式实现。
-
每一个线程有自己独立的ThreadLocalMap, 再基于ThreadLocald对象本身作为key,对value进行存取。
-
ThreadLocalMap的key是一个弱引用。弱引用的特点:即便对象有弱引用,在GC时,也必须被回收。如果ThreadLocal对象为局部变量,即方法执行结束后,threadLocal对象强引用消失,如果key的引用是强引用,会导致threadLocal对象无法被回收。如果threadLocal是静态成员变量,则不会存在这个问题。
ThreadLocal内存泄露问题:
如果threadLocal对象的引用丢失(即:使用的threadLocal不是静态成员变量,而是局部变量),key因为弱引用被GC回收掉,如果同时线程还没有被回收,就会导致内存泄露,内存中的value无法被回收,同时也无法被获取到。
解决方法:
方式一:只需要在使用完毕threadLocal对象之后,及时的调用threadLocal.remove()方法,移除该threalLocal对象key的Entry即可。(说明:threadLocal.remove()是threadLocal对象的强引用,即在移除Entry前,threalLocal对象不会失效)
方式二:将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。
总结:
线程中threadLocalMap(即threadLocals),是在第一次调用ThreadLocal的get或set方法时,才会创建。
线程调用很深,但又不想传参时,可以使用threadLocals。