ThreadLocal之通俗易懂篇
概念
ThreadLocal字面上意思是线程本地,它的存在并不是为了来解决多线程环境的共享变量问题,而是来提供线程内部的存储空间。在多线程环境中,可以保证各个线程之间的变量相互隔离,互相独立。
初步了解ThreadLocal源码
在ThreadLocal中有四个常用的方法:1 get() 2 set() 3 initialValue(初始化) 4 remove
//下面演示的是初始化ThreadLocal的值
public class ThreadLocalTest {
public static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return Integer.valueOf(1);
}
};
}
关于ThreadLocal的源码实现,我们正常的思维就是ThreadLocal内部类维护一个线程安全的集合,比如Map。用线程ID作为Map的key,实例对象作为Map的value,这样就可以实现线程之间相互隔离的效果。但是其实这种方法现在早已经没有被沿用了
那现在采用的何种方种?我们先从get方法看起
public T get() {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t); //这里返回当前线程的threadLocals变量
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();//如果上述map为空,就返回我们的初始化值
}
//接下来看看它的getMap方法
ThreadLocakMap getMap(Thread t){
return t.threadLocals;
}
//上述代码的threadLocals是Thread类的成员变量,初始化为null
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap是ThreadLocal的静态内部类,每个Thread维护一个ThreadLocalMap映射表,这个映射表的key是ThreadLocal实例本身,value是真正需要存储的Object。
这样设计的好处是当Thread销毁后,对应的ThreadLocalMap也就随之销毁
初步了解ThreadLocalMap源码
static class Entry extends WerkReference<ThreadLocal<?>>{
Object value;
Entry(ThreadLocal<?> k,Object v){
super(k);
value = v;
}
}
//从上述代码可知道,使用ThreadLocal的弱引用作为key
下面是一个引用图,实线为强引用,虚线为弱引用
ThreadLocalMap使用ThreadLocal的弱引用作为key,在虚拟机运行垃圾回收时候,这个ThreadLocal会被回收,这样ThreadLocalMap中就会出现key为null的Entry,这些key对应的value也就再无法访问,但是value中还存在一条从Current Thread过来的强引用链,只有当Current Thread销毁的时候,value才能被释放
那么ThreadLocalMap中如何解决这个问题?
//首先getEntry 计算存储Key的Entry的索引位置,然后判断key是否存在,不存在则调用下面的办法
//getEntryAfterMiss
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get(); //用值来反查key,比较少见
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}|
若是发现key为null,说明弱引用key被回收了,这个时候会调用expungeStaleEntry(int) 在这个函数中有下面两句代码,来清理key与其对应的value。最好的方式是将ThreadLocal变量定义成private static。这样ThreadLocal的生命周期更长,一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收。
tab[staleSlot].value = null;
tab[staleSlot] = null;
个人理解:一个Thread中可以创建多个ThreadLocal用来存储不同的数据,但是他们获得的ThreadLocalMap都是同一个。多个ThreadLocal也能够创建多个Entry。
InheritableThreadLocal
InheritableThreadLocal继承自ThreadLocal,使用InheritableThreadLocal类可以使子线程继承父线程的值