/** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ staticclassEntryextendsWeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value;
/** * The initial capacity -- MUST be a power of two. */ privatestaticfinalint INITIAL_CAPACITY = 16;
/** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table;
/** * The number of entries in the table. */ privateint size = 0;
/** * The next size value at which to resize. */ privateint threshold; // Default to 0
ThreadLocal内部是由entry这个弱引用对象组成,这个entry就是一个hash map,只是是弱引用。Entry的构造函数Entry(ThreadLocal<?> k, Object v),ThreadLocal引用作为key。根据注释key=null,则不是强引用,因为是弱引用,entry can be expunged from table,这个对象从table中删除。
ThreadLocal#nextHashCode
1 2 3 4 5 6
/** * The next hash code to be given out. Updated atomically. Starts at * zero. */ privatestatic AtomicInteger nextHashCode = new AtomicInteger();
获取下1个hashcode的值。
ThreadLocal#HASH_INCREMENT
1 2 3 4 5 6
/** * The difference between successively generated hash codes - turns * implicit sequential thread-local IDs into near-optimally spread * multiplicative hash values for power-of-two-sized tables. */ privatestaticfinalint HASH_INCREMENT = 0x61c88647;
ThreadLocal#threadLocalHashCode
1 2 3 4 5 6 7 8 9 10 11
/** * ThreadLocals rely on per-thread linear-probe hash maps attached * to each thread (Thread.threadLocals and * inheritableThreadLocals). The ThreadLocal objects act as keys, * searched via threadLocalHashCode. This is a custom hash code * (useful only within ThreadLocalMaps) that eliminates collisions * in the common case where consecutively constructed ThreadLocals * are used by the same threads, while remaining well-behaved in * less common cases. */ privatefinalint threadLocalHashCode = nextHashCode();
/** * Returns the value in the current thread's copy of this * thread-local variable. If the variable has no value for the * current thread, it is first initialized to the value returned * by an invocation of the {@link #initialValue} method. * * @return the current thread's value of this thread-local */ public T get(){ Thread t = Thread.currentThread(); // 1 ThreadLocalMap map = getMap(t); if (map != null) { // 2 ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }
标注代码分析
获取当前线程的ThreadLocal#ThreadLocalMap。
threadlocal#ThreadLocalMap的entry对象。
ThreadLocal#getMap()
1 2 3 4 5 6 7 8 9 10
/** * Get the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @return the map */ ThreadLocalMap getMap(Thread t){ return t.threadLocals; }
/** * Get the entry associated with key. This method * itself handles only the fast path: a direct hit of existing * key. It otherwise relays to getEntryAfterMiss. This is * designed to maximize performance for direct hits, in part * by making this method readily inlinable. * * @param key the thread local object * @return the entry associated with key, or null if no such */ private Entry getEntry(ThreadLocal key){ // 1 int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }
标注代码分析
减少散列冲突。
ThreadLocal#set()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/** * Sets the current thread's copy of this thread-local variable * to the specified value. Most subclasses will have no need to * override this method, relying solely on the {@link #initialValue} * method to set the values of thread-locals. * * @param value the value to be stored in the current thread's copy of * this thread-local. */ publicvoidset(T value){ Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }
ThreadLocal#createMap()
1 2 3 4 5 6 7 8 9 10 11
/** * Create the map associated with a ThreadLocal. Overridden in * InheritableThreadLocal. * * @param t the current thread * @param firstValue value for the initial entry of the map * @param map the map to store. */ voidcreateMap(Thread t, T firstValue){ t.threadLocals = new ThreadLocalMap(this, firstValue); }
/** * Set the value associated with key. * * @param key the thread local object * @param value the value to be set */ privatevoidset(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(); // 1 if (k == key) { e.value = value; return; } // 2 if (k == null) { replaceStaleEntry(key, value, i); return; } } // 3 tab[i] = new Entry(key, value); int sz = ++size; if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
/** * Replace a stale entry encountered during a set operation * with an entry for the specified key. 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 * @param staleSlot index of the first stale entry encountered while * searching for key. */ 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). // 1 int slotToExpunge = staleSlot; // 2 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)) { 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. // 3 if (k == key) { e.value = value; // 4 tab[i] = tab[staleSlot]; tab[staleSlot] = e;
// Start expunge at preceding stale entry if it exists // 5 if (slotToExpunge == staleSlot) slotToExpunge = i; // 6 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; }
// If key not found, put new entry in stale slot // 7 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); }
/** * 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 * @return the index of the next null slot after staleSlot * (all between staleSlot and this slot will have been checked * for expunging). */ privateintexpungeStaleEntry(int staleSlot){ Entry[] tab = table; int len = tab.length;
// Rehash until we encounter null // 1 Entry e; int i; for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) { ThreadLocal k = e.get(); // 2 if (k == null) { e.value = null; tab[i] = null; size--; } else { // 3 int h = k.threadLocalHashCode & (len - 1); // 4 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; }
/** * Heuristically scan some cells looking for stale entries. * This is invoked when either a new element is added, or * another stale one has been expunged. It performs a * logarithmic number of scans, as a balance between no * scanning (fast but retains garbage) and a number of scans * proportional to number of elements, that would find all * garbage but would cause some insertions to take O(n) time. * * @param i a position known NOT to hold a stale entry. The * scan starts at the element after i. * * @param n scan control: <tt>log2(n)</tt> cells are scanned, * unless a stale entry is found, in which case * <tt>log2(table.length)-1</tt> additional cells are scanned. * When called from insertions, this parameter is the number * of elements, but when from replaceStaleEntry, it is the * table length. (Note: all this could be changed to be either * more or less aggressive by weighting n instead of just * using straight log n. But this version is simple, fast, and * seems to work well.) * * @return true if any stale entries have been removed. */ privatebooleancleanSomeSlots(int i, int n){ boolean removed = false; Entry[] tab = table; int len = tab.length; do { i = nextIndex(i, len); Entry e = tab[i]; // 1 if (e != null && e.get() == null) { n = len; removed = true; i = expungeStaleEntry(i); } } while ( (n >>>= 1) != 0); return removed; }
标注代码分析
清除entry!=null,但是entry#value = null的值。
程序实例
证明每个线程在ThreadLocal都是私有的。set单值和多值,tab的下标变化。
同一个线程多个值
tab下标的变化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
package threadLocal;
publicclassT{
protectedstatic ThreadLocal<String> threadLocal = new ThreadLocal<String>();