/** * 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() { Threadt= Thread.currentThread(); ThreadLocalMapmap= getMap(t); // 每个线程私有 if (map != null) { ThreadLocalMap.Entrye= map.getEntry(this); // 以当前ThreadLocal对象为key, 获取Entry, 其中存放了value if (e != null) { @SuppressWarnings("unchecked") Tresult= (T)e.value; return result; } } return setInitialValue(); }
此处说明 ThreadLocalMap 采用的是懒加载模式, 用时再去初始化
ThreadLocalMap#getEntry()源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/** * 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) { inti= key.threadLocalHashCode & (table.length - 1); Entrye= table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }
/** * 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. */ privatefinalintthreadLocalHashCode= nextHashCode();
/** * The next hash code to be given out. Updated atomically. Starts at * zero. */ privatestaticAtomicIntegernextHashCode= newAtomicInteger();
/** * 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. */ privatestaticfinalintHASH_INCREMENT=0x61c88647;
/** * Returns the next hash code. */ privatestaticintnextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }
/** * Version of getEntry 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 entry associated with key, or null if no such */ private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) { Entry[] tab = table; intlen= tab.length;
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]; } returnnull; }
privatestaticintnextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); }
开启 while 循环, 当 e 的 key 与当前线程对象相等时, 返回 e
如果 e 的 key 等于 null, 开启探测式清理, 也就是 expungeStaleEntry() 方法
/** * 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; intlen= 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(); if (k == null) { e.value = null; tab[i] = null; size--; } else { inth= 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; }
/** * Variant of set() to establish initialValue. Used instead * of set() in case user has overridden the set() method. * * @return the initial value */ private T setInitialValue() { Tvalue= initialValue(); // 如果初始化ThreadLocal时没有重写就返回null Threadt= Thread.currentThread(); ThreadLocalMapmap= getMap(t); if (map != null) { map.set(this, value); } else { createMap(t, value); } if (thisinstanceof TerminatingThreadLocal) { TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this); } return value; }
ThreadLocal#createMap()源码
1 2 3 4 5 6 7 8 9 10
/** * 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 */ voidcreateMap(Thread t, T firstValue) { t.threadLocals = newThreadLocalMap(this, firstValue); }
ThreadLocal#set()源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
/** * 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) { Threadt= Thread.currentThread(); ThreadLocalMapmap= getMap(t); if (map != null) { map.set(this, value); } else { createMap(t, value); } }
/** * 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.
/** * Removes the current thread's value for this thread-local * variable. If this thread-local variable is subsequently * {@linkplain #get read} by the current thread, its value will be * reinitialized by invoking its {@link #initialValue} method, * unless its value is {@linkplain #set set} by the current thread * in the interim. This may result in multiple invocations of the * {@code initialValue} method in the current thread. * * @since 1.5 */ publicvoidremove() { ThreadLocalMapm= getMap(Thread.currentThread()); if (m != null) { m.remove(this); } }
ThreadLocal#remove()源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
/** * Remove the entry for key. */ privatevoidremove(ThreadLocal<?> key) { Entry[] tab = table; intlen= tab.length; inti= key.threadLocalHashCode & (len-1); for (Entrye= tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { if (e.get() == key) { e.clear(); // this.referent = null; expungeStaleEntry(i); return; } } }