ThreadLocal相當於提供了一種線程隔離,將變量與線程相綁定,它可以保證訪問到的變量屬於當前線程,每個線程都保存有一個變量副本,每個線程的變量都不同,而同一個線程在任何時候訪問這個本地變量的結果都是一致的。當此線程結束生命週期時,所有的線程本地實例都會被GC。
ThreadLocal 的基本用法
public class ThreadLocal {
private static java.lang.ThreadLocal<Integer> seqNum = new java.lang.ThreadLocal<Integer>() {
@Override
protected Integer initialValue() {
return 0;
}
};
private int getNextNum() {
seqNum.set(seqNum.get() + 1);
return seqNum.get();
}
public static void main(String[] args) {
ThreadLocal sn = new ThreadLocal();
ThreadId t1 = new ThreadId(sn);
ThreadId t2 = new ThreadId(sn);
ThreadId t3 = new ThreadId(sn);
t1.start();
t2.start();
t3.start();
}
private static class ThreadId extends Thread {
private ThreadLocal sn;
public ThreadId(ThreadLocal sn) {
this.sn = sn;
}
@Override
public void run() {
for (int i = 0; i < 3 ; i++) {
System.out.println("thread[" + Thread.currentThread().getName() + "] --> sn["
+ sn.getNextNum() + "]");
}
}
}
}
輸出結果
thread[Thread-0] --> sn[1]
thread[Thread-1] --> sn[1]
thread[Thread-1] --> sn[2]
thread[Thread-0] --> sn[2]
thread[Thread-0] --> sn[3]
thread[Thread-1] --> sn[3]
thread[Thread-2] --> sn[1]
thread[Thread-2] --> sn[2]
thread[Thread-2] --> sn[3]
ThreadLocal 源碼
private final int threadLocalHashCode = nextHashCode();
/**
* The next hash code to be given out. Updated atomically. Starts at
* zero.
*/
private static AtomicInteger nextHashCode =
new AtomicInteger();
/**
* 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.
*/
private static final int HASH_INCREMENT = 0x61c88647;
/**
* Returns the next hash code.
*/
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
threadLocalHashCode 用來標識ThreadLocal的唯一性,每次hash操作的增量爲0x61c88647。
/**
* 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.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
通過當前線程t 來獲取 ThreadLocalMap ,調用的方法是 getMap(t), getMap 的代碼如下:
/**
* 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;
}
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
返回的是Thread 中的 threadLocals 屬性,每個Thread裏面都有一個ThreadLocal.ThreadLocalMap成員變量,也就是說每個線程通過ThreadLocal.ThreadLocalMap與ThreadLocal相綁定,這樣可以確保每個線程訪問到的thread-local variable都是本線程的。
第一次獲取threadLocals 爲空,這個時候調用
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;
}
我們來分析 createMap
/**
* 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.
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
下面分析 ThreadLocalMap ,它是ThreadLocal的靜態內部類。
成員變量與常量
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16; //Map 的初始容量,必須是2的N次冪
/**
* 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; //table 中數據的數量
/**
* The next size value at which to resize.
*/
private int threshold; // Default to 0 //擴容時對應的閾值
下面分析存儲數據的類Entry 代碼如下 :
/**
* 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.
*/
static class Entry extends WeakReference<ThreadLocal> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal k, Object v) {
super(k);
value = v;
}
}
Entry類繼承了WeakReference
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);
}
第一個參數就是本ThreadLocal實例(this),第二個參數就要保存的Value。構造函數首先創建一個長度爲16的Entry數組,然後計算出firstKey對應的哈希值,然後存儲到table中,並設置size和threshold。
剩下的操作就是對ThreadLocalMap操作,報考set get等
ThreadLocal 的分析到這裏就結束了,下面看看用法