源码比较简单,但是里面有很多优化的地方。
TheadLocal.get()的时候实际上拿到当前线程的threadLocalMap,以theadLocal为key,取value的过程。
源码为证:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
但是ThreadLocalMap是一个静态内部类。其中的entry是一个weakReference来看看ThreadLocal是怎么处理的,判断是否是statle Entry的代码很简单,就是entry.get()是否为空:
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// expunge entry at staleSlot
tab[staleSlot].value = null;
tab[staleSlot] = null;
size--;
// Rehash until we encounter null
Entry e;
int i;
for (i = nextIndex(staleSlot, len);
(e = tab[i]) != null;
i = nextIndex(i, len)) {
ThreadLocal k = e.get();
if (k == null) {
e.value = null;
tab[i] = null;
size--;
} else {
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
tab[i] = null;
// Unlike Knuth 6.4 Algorithm R, we must scan until
// null because multiple entries could have been stale.
while (tab[h] != null)
h = nextIndex(h, len);
tab[h] = e;
}
}
}
return i;
}
可以看到会把从staleSlot到null entry这一段内所有的stale entry都删除掉,并且非stale的entry会重新rehash。保证stale slot所在的run(指两个null entry之间的所有entry)内不再有stale slot。但不是删除map内所有的stale entry。
另外一个删除stale entry的方法是:
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
Entry e = tab[i];
if (e != null && e.get() == null) {
n = len;
removed = true;
i = expungeStaleEntry(i);
}
} while ( (n >>>= 1) != 0);
return removed;
}
可以看到内部调用了expungeStaleEntry,但是也有漏掉statle entry的情况,比如说在从i开始的log2(n)都不存在stale entry,但是其他地方存在stale的场景。
在get或者set中会调用上面的两个方法来删除部分stale,但是有可能不是全部。
所以存在内存泄露,记的把TheadLocal设置为static,然后调用remove方法。