ThreadLocal
ThreadLocal存储当前线程私有的数据
Thread类中有一个默认(包级别)访问权限的字段:ThreadLocals
,它是ThreadLocalMap
类型的。
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap是ThreadLocal的静态内部类。key是弱引用
set操作
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
- ThreadLocalMap里面的table数组的负载因子是2/3,如果超过threshold,会先清理无用的Entry,如果还不够就两倍扩容
private void setThreshold(int len) { threshold = len * 2 / 3; }
get操作
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类型的属性ThreadLocals,而ThreadLocalMap是ThreadLocal的静态内部类,它内部有一个静态内部类Entry,继承于WeakReference,Entry里面的key就是弱引用的ThreadLocal对象,value是设定的值。有一个Entry类型的table数组用于存放所有线程对应的Entry,当前线程对应的Entry在数组中的位置是通过key的哈希值与数组长度取模确定的。set和get方法就是从table数组中合适的位置放入Entry或者是获取value。
弱引用问题:
每个Thread内部都维护一个ThreadLocalMap数据结构,Map的Key就是ThreadLocal,ThreadLocal内部存储实体结构Entry<ThreadLocal, T>继承自java.lan.ref.WeakReference,这样当ThreadLocal不再被引用时,因为弱引用机制
原因,垃圾回收时,jvm会自动回收弱引用指向的实例内存,即其线程内部的ThreadLocalMap会释放其对ThreadLocal的引用回收ThreadLocal对象,而非整个Entry,所以线程变量中的值T
对象还是在内存中存在的,所以内存泄漏
的问题还没有完全解决。应该尽量用threadLocal.remove()来清除无用的Entry。