赞
踩
ThreadLocal
主要作用就是实现线程间变量隔离,对于一个变量,每个线程维护一个自己的实例,防止多线程环境下的资源竞争,那ThreadLocal
是如何实现这一特性的呢?
图1
从上图可知:
Thread
对象中都包含一个ThreadLocal.ThreadLocalMap
类型的threadlocals
成员变量;Entry
对象中:key是ThreadLocal
对象的弱引用,value是该threadlocal
变量在当前线程中的对应的变量实体;ThreadLocal
对象对应的变量时,首先从当前线程对象中获取对应的threadlocals
哈希表,再以该ThreadLocal
对象为key查询哈希表中对应的value;threadlocals
哈希表,因此线程间ThreadLocal
对象对应的变量实体也是独占的,不存在竞争问题,也就避免了多线程问题。有人可能会问:ThreadLocalMap
是Thread
成员变量(非public,只有包访问权限,Thread和Threadlocal都在java.lang 包下,Thread可以访问ThreadLocal.ThreadLocalMap),定义却在ThreadLocal中,为什么要这么设计?
源码的注释给出了解释:ThreadLocalMap
就是维护线程本地变量设计的,就是让使用者知道ThreadLocalMap
就只做保存线程局部变量这一件事。
public void set(T value) {
Thread t = Thread.currentThread(); //获取当前线程
ThreadLocalMap map = getMap(t); //从当前线程对象中获取threadlocals,该map保存了所用的变量实例
if (map != null) {
map.set(this, value);
} else {
createMap(t, value); //初始threadlocals,并设置当前变量
}
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //从当前线程对象中获取threadlocals,该map保存了所用的变量实体 if (map != null) { // 获取当前threadlocal对象对应的变量实体 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } // 如果map没有初始化,那么在这里初始化一下 return setInitialValue(); }
由于通过 ThreadLocal
的 set()
设置的值,只会设置当前线程对应变量实体,无法实现统一初始化所有线程的ThreadLocal
的值。ThreadLocal
提供了一个 withInitial()
方法实现这一功能:
ThreadLocal<String> initValue = ThreadLocal.withInitial(() -> "initValue");
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
// 返回SuppliedThreadLocal类型对象
return new SuppliedThreadLocal<>(supplier);
}
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
private final Supplier<? extends T> supplier;
SuppliedThreadLocal(Supplier<? extends T> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}
@Override
protected T initialValue() {
// 获取初始化值
return supplier.get();
}
}
由图1可知,ThreadLocal.ThreadLocalMap
对应的Entry
中,key为ThreadLocal
对象的弱引用,方法执行对应栈帧中的ThreadLocal
引用为强引用。当方法执行过程中,由于栈帧销毁或者主动释放等原因,释放了ThreadLocal
对象的强引用,即表示该ThreadLocal
对象可以被回收了。又因为Entry
中key为ThreadLocal
对象的弱引用,所以当jvm执行GC操作时是能够回收该ThreadLocal
对象的。
而<
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。