ThreadLocal存储当前线程私有的数据
Thread类中有一个默认(包级别)访问权限的字段:ThreadLocals
,它是ThreadLocalMap
类型的。
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap是ThreadLocal的静态内部类。key是弱引用
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);//以ThreadLocal对象为Key,value为值
else
createMap(t, value);
}
其中主要的操作:
获取当前线程的ThreadLocalMap
对象:ThreadLocalMap map = getMap(t);
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
在map中设置当前的key和value,其实就是在table数组的对应位置放置一个Entry,这个位置是通过key的哈希值与数组长度取模确定的,如果发生冲突,就探测下一个位置。
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//如果这个位置已经存在Entry对象:
//1.如果存在的Key就是要set的key,那么更新对应的value值
//2.如果key==null,这是因为在Entry中弱引用的ThreadLocal已经被垃圾回收,但是还存在旧的value没有清理,需要替换旧的Entry
//否则,就采用开放地址法,探测下一个位置(源码其实就是i+1):i = nextIndex(i, len)是否还是e!=null,进行下一轮循环,直到e==null
//如果不存在Entry对象,就新建Entry(key,value)
for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
如果不存在ThreadLocalMap,就新建Map
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);\\1.
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);\\2.
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;\\3.
return result;
}
}
return setInitialValue();
}
其中的主要操作:
获取当前线程对应的ThreadLocalMap
:ThreadLocalMap map = getMap(t);
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
根据ThreadLocalMap的getEntry(key)方法,以当前的ThreadLocal
作为key获取Entry对象。在ThreadLocalMap类中有一个Entry类型的数组,数组每个位置都是Entry类型的数据,这里就是根据Key的哈希码与数组长度取模得到当前Key在数组中的位置,获取对应的Entry对象。
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);
}
Entry的value就是需要的值:T result = (T)e.value;
每个Thread内部都维护一个ThreadLocalMap数据结构,Map的Key就是ThreadLocal,ThreadLocal内部存储实体结构Entry<ThreadLocal, T>继承自java.lan.ref.WeakReference,这样当ThreadLocal不再被引用时,因为弱引用机制
原因,垃圾回收时,jvm会自动回收弱引用指向的实例内存,即其线程内部的ThreadLocalMap会释放其对ThreadLocal的引用回收ThreadLocal对象,而非整个Entry,所以线程变量中的值T
对象还是在内存中存在的,所以内存泄漏
的问题还没有完全解决。应该尽量用threadLocal.remove()来清除无用的Entry。
原文:https://www.cnblogs.com/learnjavajava/p/14901375.html