您的位置:首页 > 编程语言 > Java开发

java源码阅读之深入理解ThreadLocal

2019-03-31 17:34 573 查看

总结:

  1. 每一个Thread实例中都会保存一个map数据结构,map中的Entry<ThreadLocal,value>用来存储线程中用到的每个ThreadLocal的引用和其对应的value。使用set和get方法时,在Thread实例的map中查找和修改ThreadLocal对应的value值。
  2. 防止内存泄露问题:因为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为空则重新进行初始化
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: