This class provides thread-local variables. These variables differ
from their normal counterparts in that each thread that accesses one
(via its get or set method) has its own,
independently initialized copy of the variable. ThreadLocal
instances are typically private static fields in classes that wish to
associate state with a thread (e.g., a user ID or Transaction ID).
/** * 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(); //通过getMap获取ThreadLocalMap ThreadLocalMapmap= getMap(t); if (map != null) { //获取entry ThreadLocalMap.Entrye= map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") Tresult= (T)e.value; return result; } } //不存在则进行初始化 return setInitialValue(); }
publicclassThreadimplementsRunnable { /* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMapthreadLocals=null;
}
setInitialValue
get方法获取不到值时,通过该方法设置初始值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/** * 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(); Threadt= Thread.currentThread(); ThreadLocalMapmap= getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
createMap
ThreadLocalMap不存在时,通过该方法创建,这里有一个疑惑:
Q:为什么在get和setInitialValue进行两次为空检查才进行createMap?
1 2 3
voidcreateMap(Thread t, T firstValue) { t.threadLocals = newThreadLocalMap(this, firstValue); }
/** * 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;
/** * 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; intlen= tab.length; //获取hash后的下标 inti= key.threadLocalHashCode & (len-1); //遍历 for (Entrye= tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { ThreadLocal<?> k = e.get(); //如果对应下标的ThreadLocal与当前的相等,直接更新 if (k == key) { e.value = value; return; } //如果ThreadLocal对象已被回收,调用replaceStaleEntry if (k == null) { replaceStaleEntry(key, value, i); return; } } //如果遍历一圈还是找不到,新建entry进行存储 tab[i] = newEntry(key, value); intsz= ++size; //检查是否需要rehash if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); }
/** * 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 //entry不存在或key不相等 return getEntryAfterMiss(key, i, e); }
getEntryAfterMiss
也就是对table数组进行遍历查找,找一圈还没有则返回null。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
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; }
// 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; }