// Entry中key为ThreadLocal,value为值 // key是一个弱引用 // 每个线程在往ThreadLocal中放值的时候,都是放入线程本身的ThreadLocalMap中,key是ThreadLocal,从而实现了线程隔离 // 只要发生了垃圾回收,且该对象没有强引用存在的话,弱引用就会被回收 staticclassEntryextendsWeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ 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); // 如果出现hash冲突,会采用线性探测,如果当前位置有值,则会获取下一个索引来进行存储,该结构是一个环形 // 直到找到空闲位置为止 for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { // 槽位不为空,进行线性探测 ThreadLocal<?> k = e.get(); // key值与当前key值相等,直接进行覆盖 if (k == key) { e.value = value; return; } // Entry不为null,但是key为null,当前位置的key已经为null(失效了),则进行替换过期数据 if (k == null) { replaceStaleEntry(key, value, i); return; } // 该循环完成,说明既没有找到原本存储key的位置,也没有找到失效的位置 } // 执行到这里说明查找到了entry为null的位置 tab[i] = new Entry(key, value); int sz = ++size; // 没有清除掉失效的槽位,并且当前数量已经达到了阈值,则进行扩容 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
// 获取下一个数组下标位置 privatestaticintnextIndex(int i, int len){ return ((i + 1 < len) ? i + 1 : 0); }
// 替换掉失效的值 staleSlot为失效的槽位 privatevoidreplaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot){ Entry[] tab = table; int len = tab.length; Entry e;
// Back up to check for prior stale entry in current run. // We clean out whole runs at a time to avoid continual // incremental rehashing due to garbage collector freeing // up refs in bunches (i.e., whenever the collector runs). int slotToExpunge = staleSlot; // 向前扫描,直到找到无效的槽位 for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) // 找到无效的槽位,slotToExpunge复为当前位置 if (e.get() == null) slotToExpunge = i;
// Find either the key or trailing null slot of run, whichever // occurs first // 向后扫描,找到失效的槽位 for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal<?> k = e.get();
// If we find key, then we need to swap it // with the stale entry to maintain hash table order. // The newly stale slot, or any other stale slot // encountered above it, can then be sent to expungeStaleEntry // to remove or rehash all of the other entries in run. // 找到key,替换掉无效的槽位中的值 if (k == key) { e.value = value;
tab[i] = tab[staleSlot]; tab[staleSlot] = e;
// Start expunge at preceding stale entry if it exists // 向前扫描时没有找到无效的槽位,将slotToExpunge设为当前位置 if (slotToExpunge == staleSlot) slotToExpunge = i; // 从slotToExpunge开始清理槽位 cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); return; }
// If we didn't find stale entry on backward scan, the // first stale entry seen while scanning for key is the // first still present in the run. // 如果当前的槽位已经无效,并且向前扫描时没有找到无效的槽位,将slotToExpunge设为当前位置 if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; }
// 没有找到对应的key新增加一个entry // If key not found, put new entry in stale slot tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value);
// If there are any other stale entries in run, expunge them // 扫描过程中存在无效的槽位,进行清理 if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); }
// 清理无效的槽位 privatebooleancleanSomeSlots(int i, int n){ boolean removed = false; Entry[] tab = table; int len = tab.length; do { i = nextIndex(i, len); Entry e = tab[i]; // key已失效 if (e != null && e.get() == null) { n = len; removed = true; i = expungeStaleEntry(i); } } while ( (n >>>= 1) != 0); return removed; }
privateintexpungeStaleEntry(int staleSlot){ Entry[] tab = table; int len = tab.length;
// 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(); // key为null,去掉对value的引用 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; }
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 // 没有命中 // 在存放数据的时候采用的是开方定址法,所以可能存在当前key的散列值和元素所在索引并不完全对应的情况 return getEntryAfterMiss(key, i, e); }
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e){ Entry[] tab = table; int len = tab.length;
while (e != null) { ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) // key等于null,说明该key已经失效了,进行回收,可以有效的避免内存泄漏 expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } returnnull; }
remove方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
privatevoidremove(ThreadLocal<?> key){ 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)]) { // 找到对应的key if (e.get() == key) { // 清除对ThreadLocal的弱引用 e.clear(); // 清理key为null的元素 expungeStaleEntry(i); return; } } }