哈希槽为空, 直接将新 Entry 填入槽中; 另外挪用 cleanSomeSlots 搜索并清理 GC 造成的空洞; 另外查抄 Entry 数量是否达到阈值, 须要时挪用 rehash 要领举办扩容。
哈希槽中为当前 ThreadLocal, 直接举办替换
哈希槽中为空 Entry, 说明原有ThreadLocal 被 GC 接纳, 挪用 replaceStaleEntry 将其替换。
接下来重点阐明 replaceStaleEntry:
private void replaceStaleEntry(ThreadLocal<?> key, Object value, int staleSlot) { Entry[] tab = table; int len = tab.length; Entry e; int slotToExpunge = staleSlot; for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) if (e.get() == null) slotToExpunge = i; for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal<?> k = e.get(); if (k == key) { e.value = value; tab[i] = tab[staleSlot]; tab[staleSlot] = e; if (slotToExpunge == staleSlot) slotToExpunge = i; cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); return; } if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; } tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value); if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); }replaceStaleEntry 要领看上去很是巨大, 简朴的说分为三部门:
向前寻找空的 Entry 将其位置写入 slotToExpunge, 这是为了清理不必继承存眷
向后举办寻找若是找到与传入的 key 沟通 Entry 则更新 Entry 的内容并将其移动到 staleSlot, 然后挪用 cleanSomeSlots 举办清理
若最终没有找到 key 沟通的Entry, 则在 staleSlot 处写入一个新的 Entry, 挪用 cleanSomeSlots 举办清理
cleanSomeSlots 挪用 expungeStaleEntry 从位置 i 开始向后清理。
执行log2(n)次清理以取得清理结果(剩余空洞数量)和清理耗时之间的均衡。
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; }简朴看一下 rehash 的进程:
private void rehash() { expungeStaleEntries(); if (size >= threshold - threshold / 4) resize(); } 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; }首先举办清理,若清理后sz > thresholde * 0.75将哈希表的的巨细翻倍。
Removeremove 要领和 get 要领较量雷同:
private void remove(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)]) { if (e.get() == key) { e.clear(); expungeStaleEntry(i); return; } } }Linux公社的RSS地点:https://www.linuxidc.com/rssFeed.aspx