📄 threadlocal.java
字号:
int len = parentTable.length; setThreshold(len); table = new Entry[len]; for (int j = 0; j < len; j++) { Entry e = parentTable[j]; if (e != null) { Object k = e.get(); if (k != null) { ThreadLocal key = (ThreadLocal)(k); 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++; } } } } /** * Get the value associated with key with code h. This method itself * handles only the fast path: a direct hit of existing key. It * otherwise relays to getAfterMiss. This is designed to maximize * performance for direct hits, in part by making this method readily * inlinable. * * @param key the thread local object * @param h key's hash code * @return the value associated with key */ private Object get(ThreadLocal key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e.value; return getAfterMiss(key, i, e); } /** * Version of get method for use when key is not found in its * direct hash slot. * * @param key the thread local object * @param i the table index for key's hash code * @param e the entry at table[i]; * @return the value associated with key */ private Object getAfterMiss(ThreadLocal key, int i, Entry e) { Entry[] tab = table; int len = tab.length; while (e != null) { Object k = e.get(); if (k == key) return e.value; if (k == null) return replaceStaleEntry(key, null, i, true); i = nextIndex(i, len); e = tab[i]; } Object value = key.initialValue(); tab[i] = new Entry(key, value); if (++size >= threshold) rehash(); return value; } /** * Set the value associated with key. * * @param key the thread local object * @param value the value to be 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)]) { Object k = e.get(); if (k == key) { e.value = value; return; } if (k == null) { replaceStaleEntry(key, value, i, false); return; } } tab[i] = new Entry(key, value); if (++size >= threshold) rehash(); } /** * Remove the entry for key. THIS IS USED ONLY BY ThreadLocal.remove, WHICH IS NOT CURRENTLY PART OF THE PUBLIC API. IF IT IS ADDED TO THE PUBLIC API AT SOME POINT, THIS METHOD MUST BE UNCOMMENTED. 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; } } } */ /** * Replace a stale entry encountered during a get or set operation * with an entry for the specified key. If actAsGet is true and an * entry for the key already exists, the value in the entry is * unchanged; if no entry exists for the key, the value in the new * entry is obtained by calling key.initialValue. If actAsGet is * false, the value passed in the value parameter is stored in the * entry, whether or not an entry already exists for the specified key. * * As a side effect, this method expunges all stale entries in the * "run" containing the stale entry. (A run is a sequence of entries * between two null slots.) * * @param key the key * @param value the value to be associated with key; meaningful only * if actAsGet is false. * @param staleSlot index of the first stale entry encountered while * searching for key. * @param actAsGet true if this method should act as a get; false * it should act as a set. * @return the value associated with key after the operation completes. */ private Object replaceStaleEntry(ThreadLocal key, Object value, int staleSlot, boolean actAsGet) { 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)) 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)) { Object 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) { if (actAsGet) value = e.value; else e.value = value; tab[i] = tab[staleSlot]; tab[staleSlot] = e; // Start expunge at preceding stale entry if it exists if (slotToExpunge == staleSlot) slotToExpunge = i; expungeStaleEntry(slotToExpunge); return value; } // If we didn't find stale entry on backward scan, the // the first stale entry seen while scanning for key is the // first still present in the run. if (k == null && slotToExpunge == staleSlot) slotToExpunge = i; } // If key not found, put new entry in stale slot if (actAsGet) value = key.initialValue(); tab[staleSlot].value = null; // Help the GC tab[staleSlot] = new Entry(key, value); // If there are any other stale entries in run, expunge them if (slotToExpunge != staleSlot) expungeStaleEntry(slotToExpunge); return value; } /** * Expunge a stale entry by rehashing any possibly colliding entries * lying between staleSlot and the next null slot. This also expunges * any other stale entries encountered before the trailing null. See * Knuth, Section 6.4 * * @param staleSlot index of slot known to have null key */ private void expungeStaleEntry(int staleSlot) { Entry[] tab = table; int len = tab.length; // expunge entry at staleSlot tab[staleSlot].value = null; // Help the GC tab[staleSlot] = null; size--; // Rehash until we encounter null Entry e; for (int i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { Object k = e.get(); if (k == null) { e.value = null; // Help the GC tab[i] = null; size--; } else { ThreadLocal key = (ThreadLocal)(k); int h = key.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; } } } } /** * Re-pack and/or re-size the table. First scan the entire * table removing stale entries. If this doesn't sufficiently * shrink the size of the table, double the table size. */ private void rehash() { expungeStaleEntries(); // Use lower threshold for doubling to avoid hysteresis if (size >= threshold - threshold / 4) resize(); } /** * Double the capacity of the table. */ 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]; oldTab[j] = null; // Help the GC if (e != null) { Object k = e.get(); if (k == null) { e.value = null; // Help the GC } else { ThreadLocal key = (ThreadLocal)(k); int h = key.threadLocalHashCode & (newLen - 1); while (newTab[h] != null) h = nextIndex(h, newLen); newTab[h] = e; count++; } } } setThreshold(newLen); size = count; table = newTab; } /** * Expunge all stale entries in the table. */ 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); } } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -