ThreadLocal提供了線程本地變量,它可以保證訪問到的變量屬于當(dāng)前線程,每個線程都保存有一個變量副本,每個線程的變量都不同。ThreadLocal相當(dāng)于提供了一種線程隔離,將變量與線程相綁定。
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).
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
ThreadLocal 類定義如下:
public class ThreadLocal<T> {
private final int threadLocalHashCode = nextHashCode();
private static AtomicInteger nextHashCode =
new AtomicInteger();
private static final int HASH_INCREMENT = 0x61c88647;
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
public ThreadLocal() {
}
}
ThreadLocal通過threadLocalHashCode來標(biāo)識每一個ThreadLocal的唯一性。threadLocalHashCode通過CAS操作進(jìn)行更新,每次hash操作的增量為 0x61c88647(不知為何)。
接下來,看看它的set方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
通過Thread.currentThread()方法獲取了當(dāng)前的線程引用,并傳給了getMap(Thread)方法獲取一個ThreadLocalMap的實例。我們繼續(xù)跟進(jìn)getMap(Thread)方法:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
可以看到getMap(Thread)方法直接返回Thread實例的成員變量threadLocals。它的定義在Thread內(nèi)部,訪問級別為package級別:
public class Thread implements Runnable {
/* Make sure registerNatives is the first thing <clinit> does. */
private static native void registerNatives();
static {
registerNatives();
}
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
}
到了這里,我們可以看出,每個Thread里面都有一個ThreadLocal.ThreadLocalMap成員變量,也就是說每個線程通過ThreadLocal.ThreadLocalMap與ThreadLocal相綁定,這樣可以確保每個線程訪問到的thread-local variable都是本線程的。
我們往下繼續(xù)分析。獲取了ThreadLocalMap實例以后,如果它不為空則調(diào)用ThreadLocalMap.ThreadLocalMap 的set方法設(shè)值;若為空則調(diào)用ThreadLocal 的createMap方法new一個ThreadLocalMap實例并賦給Thread.threadLocals。
ThreadLocal 的 createMap方法的源碼如下:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocal 的 get 方法,源碼如下:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
通過Thread.currentThread()方法獲取了當(dāng)前的線程引用,并傳給了getMap(Thread)方法獲取一個ThreadLocalMap的實例。繼續(xù)跟進(jìn)setInitialValue()方法:
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
首先調(diào)用 initialValue()方法來初始化,然后 通過Thread.currentThread()方法獲取了當(dāng)前的線程引用,并傳給了getMap(Thread)方法獲取一個ThreadLocalMap的實例,并將 初始化值存到ThreadLocalMap 中。
initialValue() 源碼如下:
protected T initialValue() {
return null;
}
下面我們探究一下ThreadLocalMap的實現(xiàn)。
ThreadLocalMap
ThreadLocalMap是ThreadLocal的靜態(tài)內(nèi)部類,源碼如下:
public class ThreadLocal<T> {
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int 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.
*/
private int size = 0;
/**
* The next size value at which to resize.
*/
private int threshold; // Default to 0
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
}
}
其中INITIAL_CAPACITY代表這個Map的初始容量;1是一個Entry類型的數(shù)組,用于存儲數(shù)據(jù);size代表表中的存儲數(shù)目;threshold代表需要擴(kuò)容時對應(yīng)size的閾值。
Entry類是ThreadLocalMap的靜態(tài)內(nèi)部類,用于存儲數(shù)據(jù)。
Entry類繼承了WeakReference<ThreadLocal<?>>,即每個Entry對象都有一個ThreadLocal的弱引用(作為key),這是為了防止內(nèi)存泄露。一旦線程結(jié)束,key變?yōu)橐粋€不可達(dá)的對象,這個Entry就可以被GC了。
接下來我們來看ThreadLocalMap 的set方法的實現(xiàn):
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();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
ThreadLocal 的get方法會調(diào)用 ThreadLocalMap 的 getEntry(ThreadLocal key) ,其源碼如下:
private Entry getEntry(ThreadLocal key) {
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);
}
private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) {
Entry[] tab = table;
int len = 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];
}
return null;
}