上篇了解通过源码,使用场景等等了解下ThreadLocal(地址:http://blog.csdn.net/hb_peng/article/details/52188960)
其中讲到ThreadLocalMap继承了软应用类,但是为什么这么做,原理是什么却并没有讲。
首先了解下WeakReference,在 http://www.jianshu.com/p/c0ca0db80299 里有这样的解释:
WeakReference是Java语言规范中为了区别直接的对象引用(程序中通过构造函数声明出来的对象引用)而定义的另外一种引用关系。WeakReference标志性的特点是:reference实例不会影响到被应用对象的GC回收行为(即只要对象被除WeakReference对象之外所有的对象解除引用后,该对象便可以被GC回收),只不过在被对象回收之后,reference实例想获得被应用的对象时程序会返回null。
这样使用弱应用的目的就很明显了,当ThreadLocal对象不再使用,可以被GC的时候,对应的在线程中ThreadLocalMap里的键值对也会失效。然而ThreadLocal对象回收后,所有对于该对象的弱引用只是变成了null,ThreadLocalMap对此是如何处理的呢?
下面是ThreadLocalMap的set方法:
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
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)]) {
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();
}
ThreadLocal执行set方法的时候,遍历map中的table时可能会发现好多键为空的Entry,即key为空。这说明这条记录已经可以被删掉了,这个时候就可以用新的Entry替换掉旧的。
此外,在resize方法中也对key为空的情况有处理:
/**
* Double the capacity of the table.
*/
private void resize() {
Entry[] oldTab = table;
int oldLen = oldTab.length;
int newLen = oldLen * 2;
Entry[] newTab = new Entry[newLen];
int count = 0;
for (int j = 0; j < oldLen; ++j) {
Entry e = oldTab[j];
if (e != null) {
ThreadLocal k = e.get();
if (k == null) {
e.value = null; // Help the GC
} else {
int h = k.threadLocalHashCode & (newLen - 1);
while (newTab[h] != null)
h = nextIndex(h, newLen);
newTab[h] = e;
count++;
}
}
}
setThreshold(newLen);
size = count;
table = newTab;
}
当键为空时(k == null),将value也置空,这样jvm就可以对之前value指向的对象进行回收了。