总结:
- 每一个Thread实例中都会保存一个map数据结构,map中的Entry<ThreadLocal,value>用来存储线程中用到的每个ThreadLocal的引用和其对应的value。使用set和get方法时,在Thread实例的map中查找和修改ThreadLocal对应的value值。
- 防止内存泄露问题:因为map中的Entry对象存储的是ThreadLocal的弱引用,所以在gc时ThreadLocal有可能被回收,之后其对应的Entry<ThreadLocal, value>中的ThreadLocal就为null,但是Entry却由于存在强引用而不能被回收。所以在使用get和set
方法时,如果遇见了ThreadLocal == null的Entry直接将其删除。很多情况下需要使用者手动调用ThreadLocal的remove函数,手动删除不再需要的ThreadLocal来防止内存泄露。因此JDK建议将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,由于一直存在ThreadLocal的强引用,所以ThreadLocal也就不会被回收,也就能保证任何时候都能根据ThreadLocal的弱引用访问到Entry的value值,然后remove它,防止内存泄露。
先看简介
ThreadLocals rely on per-thread linear-probe hash maps attached to each thread (Thread.threadLocals and inheritableThreadLocals). The ThreadLocal objects act as keys, searched via threadLocalHashCode.
- ThreadLocal们依赖关联到每个线程上的哈希表。其中以ThreadLocal对象为key,通过threadLocalHashCode来搜索。
从初始化函数切入
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);
}
if (this instanceof TerminatingThreadLocal) {
TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);
}
return value;
}
- 我们可以看到使用了ThreadLocalMap这个对象,其为ThreadLocal的内部静态类,阅读后得知其中有Entry类为弱引用(防止垃圾回收无法回收Entry对象)
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
- 继续阅读初始化函数,通过getMap获取map对象
ThreadLocal.ThreadLocalMap threadLocals = null;
- 每一个Thread对象都保存了一个map用来保存ThreadLocal和value副本,如果map为null则在Thread的map中添加一个新的Entry对象(稍后会解释)
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
- 注意:创建map中新的Entry<key,value>对象的key不是Thread实例的hashcode而是ThreadLocalHashCode
- 这样每个Thread通过维持一个map数据结构就拥有其所有的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);
}
}
- 依旧从Thread实例中获取map,如果没有该变量则在map中新建一个Entry<ThreadLocalHashCode, value>,如果有的话就修改
get方法
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();
}
- 如果map不为空则获取LocalThreadMap中的Entry对象,以ThreadLocalHashCode在map中匹配value,如果map为空则重新进行初始化