ThreadLocal源码阅读
首先,从set方法入手,
// ThreadLocal
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//这里可以看出,从Thread对象获取了一个ThreadLocalMap
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
//Thread
ThreadLocal.ThreadLocalMap threadLocals = null;
接下来,看如何创建的 这个特殊的Map
首次创建
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY];//初始化空间为16 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);//机损索引 table[i] = new Entry(firstKey, firstValue);//赋值 size = 1; setThreshold(INITIAL_CAPACITY);//计算扩容的临界点 }
注意点:Entry 的 key 采用弱引用,在内存不足时候,会被回收
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k);// k 采用弱引用,在内存不足时候,会被回收 value = v; } }
到这里,细节不说 应该了解大体的数据结构了
每个线程独有一个 Map,Map里的存储结构为 Entry <ThreadLocal,Object> 数组
如果已存在,
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)]) {//循环,直到i位置无数据 ThreadLocal<?> k = e.get(); if (k == key) {//同一个ThreadLocal 替换 value e.value = value; return; } if (k == null) {// k 为空,替换 replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold)//清除无效Entry 并扩容 rehash(); }
扩容
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; }
基于逻辑完成,
get就比较简单了
先获取 Map ,然后根据 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) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
关于ThreadLocal 内存溢出问题,一般只会出现在使用 线程池的时候,ThreadLocalMap 一直得不到释放,即使 key 因为软引用,但value没有释放造成的,但可以通过执行 remove 方法来主动释放