ThreadLocal源码分析-黄金分割数的使用 (3)

这里注意到十分重要的一点:ThreadLocalMap$Entry是WeakReference(弱引用),并且键值Key为ThreadLocal<?>实例本身,这里使用了无限定的泛型通配符

接着看ThreadLocalMap的构造函数:

// 构造ThreadLocal时候使用,对应ThreadLocal的实例方法void createMap(Thread t, T firstValue) ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { // 哈希表默认容量为16 table = new Entry[INITIAL_CAPACITY]; // 计算第一个元素的哈希码 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); } // 构造InheritableThreadLocal时候使用,基于父线程的ThreadLocalMap里面的内容进行提取放入新的ThreadLocalMap的哈希表中 // 对应ThreadLocal的静态方法static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) private ThreadLocalMap(ThreadLocalMap parentMap) { Entry[] parentTable = parentMap.table; int len = parentTable.length; setThreshold(len); table = new Entry[len]; // 基于父ThreadLocalMap的哈希表进行拷贝 for (Entry e : parentTable) { if (e != null) { @SuppressWarnings("unchecked") ThreadLocal<Object> key = (ThreadLocal<Object>) e.get(); if (key != null) { Object value = key.childValue(e.value); Entry c = new Entry(key, value); int h = key.threadLocalHashCode & (len - 1); while (table[h] != null) h = nextIndex(h, len); table[h] = c; size++; } } } }

这里注意一下,ThreadLocal的set()方法调用的时候会懒初始化一个ThreadLocalMap并且放入第一个元素。而ThreadLocalMap的私有构造是提供给静态方法ThreadLocal#createInheritedMap()使用的。

接着看ThreadLocalMap提供给ThreadLocal使用的一些实例方法:

// 如果Key在哈希表中找不到哈希槽的时候会调用此方法 private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; int len = tab.length; // 这里会通过nextIndex尝试遍历整个哈希表,如果找到匹配的Key则返回Entry // 如果哈希表中存在Key == null的情况,调用expungeStaleEntry进行清理 while (e != null) { ThreadLocal<?> k = e.get(); if (k == key) return e; if (k == null) expungeStaleEntry(i); else i = nextIndex(i, len); e = tab[i]; } return null; } // 1.清空staleSlot对应哈希槽的Key和Value // 2.对staleSlot到下一个空的哈希槽之间的所有可能冲突的哈希表部分槽进行重哈希,置空Key为null的槽 // 3.注意返回值是staleSlot之后的下一个空的哈希槽的哈希码 private int expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // expunge entry at staleSlot // 清空staleSlot对应哈希槽的Key和Value tab[staleSlot].value = null; tab[staleSlot] = null; size--; // Rehash until we encounter null // 下面的过程是对staleSlot到下一个空的哈希槽之间的所有可能冲突的哈希表部分槽进行重哈希,置空Key为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的哈希槽中Entry的值 private void replaceStaleEntry(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; // 这个循环主要是为了找到staleSlot之前的最前面的一个Key为null的哈希槽的哈希码 for (int i = prevIndex(staleSlot, len); (e = tab[i]) != null; i = prevIndex(i, len)) if (e.get() == null) slotToExpunge = i; // Find either the key or trailing null slot of run, whichever // occurs first // 遍历staleSlot之后的哈希槽,如果Key匹配则用输入值替换 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. if (k == key) { e.value = value; tab[i] = tab[staleSlot]; tab[staleSlot] = e; // Start expunge at preceding stale entry if it exists if (slotToExpunge == staleSlot) slotToExpunge = i; 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. if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; } // Key匹配不了,则新创建一个哈希槽 // If key not found, put new entry in stale slot tab[staleSlot].value = null; tab[staleSlot] = new Entry(key, value); // 这里如果当前的staleSlot和找到前置的slotToExpunge不一致会进行一次清理 // If there are any other stale entries in run, expunge them if (slotToExpunge != staleSlot) cleanSomeSlots(expungeStaleEntry(slotToExpunge), len); } // 对当前哈希表中所有的Key为null的Entry调用expungeStaleEntry private void expungeStaleEntries() { Entry[] tab = table; int len = tab.length; for (int j = 0; j < len; j++) { Entry e = tab[j]; if (e != null && e.get() == null) expungeStaleEntry(j); } } // 清理第i个哈希槽之后的n个哈希槽,如果遍历的时候发现Entry的Key为null,则n会重置为哈希表的长度,expungeStaleEntry有可能会重哈希使得哈希表长度发生变化 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; } /** * 这个方法主要给`ThreadLocal#get()`调用,通过当前ThreadLocal实例获取哈希表中对应的Entry * */ private Entry getEntry(ThreadLocal<?> key) { // 计算Entry的哈希值 int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else // 注意这里,如果e为null或者Key对不上,会调用getEntryAfterMiss return getEntryAfterMiss(key, i, e); } // 重哈希,必要时进行扩容 private void rehash() { // 清理所有空的哈希槽,并且进行重哈希 expungeStaleEntries(); // Use lower threshold for doubling to avoid hysteresis // 哈希表的哈希元素个数大于3/4阈值时候触发扩容 if (size >= threshold - threshold / 4) resize(); } // 扩容,简单的扩大2倍的容量 private void resize() { Entry[] oldTab = table; int oldLen = oldTab.length; int newLen = oldLen * 2; Entry[] newTab = new Entry[newLen]; int count = 0; for (Entry e : oldTab) { 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; } // 基于ThreadLocal作为key,对当前的哈希表设置值,此方法由`ThreadLocal#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(); // Key匹配,直接设置值 if (k == key) { e.value = value; return; } // 如果Entry的Key为null,则替换该Key为当前的key,并且设置值 if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); int sz = ++size; // 清理当前新设置元素的哈希槽下标到sz段的哈希槽,如果清理成功并且sz大于阈值则触发扩容 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zywdgj.html