ThreadLocal 源码解读
之前对于ThreadLocal的用法和原理都没有深入的理解,因此准备学习一番有关于ThreadLocal的知识。
ThreadLocal是什么?
它为线程提供了线程本地的变量,每个线程都保存有一个变量副本,使得同一个线程在任何时刻访问它的时候都是一致的。它的生命周期跟线程绑定在一起,线程结束生命周期,该变量副本也会被GC。简单说 threadlocal提供了一个线程隔离与线程绑定。通常定义为 private static 类型
主要变量及方法 ##
private final int threadLocalHashCode = nextHashCode();
通过此变量保证threadLocal的唯一性,并通过CAS操作更新,增量为 HASH_INCREMENT = 0x61c88647 方法 构造函数
ThreadLocal threadLocal = new 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);
}
ThreadLocalMap getMap(Thread t) {
return t. threadLocals;
}
void createMap(Thread t, T firstValue) {
t. threadLocals = new ThreadLocalMap(this, firstValue);
}
threadLocalMap
Entry[] table // entry数组 用于存储数据 Entry(ThreadLocal
private static int nextIndex (int i, int len) {
return ((i + 1 < len) ? i + 1 : 0 );
}
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();
}
ThreadLocalMap.getEntry()
hash以后如果是ThreadLocal对应的Entry就返回,没有找到就调用getEntryAfterMiss()线性探测寻找 return entry || null remove()
public void remove () {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null )
m.remove(this );
}
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里都含有一个ThreadLocalMap的成员变量,这种机制将ThreadLocal和线程巧妙地绑定在了一起,即可以保证无用的ThreadLocal被及时回收,不会造成内存泄露,又可以提升性能。 假如我们把ThreadLocalMap做成一个Map,那么它存储的东西将会非常多(相当于一张全局线程本地变量表),这样的情况下用线性探测法解决哈希冲突的问题效率会非常差。而JDK里的这种利用ThreadLocal作为key,再将ThreadLocalMap与线程相绑定的实现,完美地解决。 关于GC
无用 Entry 什么时候会被清理
线程结束 调用ThreadLocalMap的remove方法或set(null)时 插入元素时,发现staled entry,则会进行替换并清理 插入元素时,map大小达到阈值,并且没有任何staled entries的时候,会调用rehash方法清理并扩容 应用场景
实现单个线程单例以及单个线程上下文信息存储,比如交易id等 实现线程安全,非线程安全的对象使用ThreadLocal之后就会变得线程安全,因为每个线程都会有一个对应的实例 承载一些线程相关的数据,避免在方法中来回传递参数 在线程池中 Thread不关闭 一般都会调用remove移除 空间换时间思想 InheritableThreadLocal 能够复制父类的数据 判断内存泄露的标准应该是 一个对象不再需要了,却无法回收依然存在才可以称之为内存泄漏, 参考