ThreadLocal實現原理
1.ThreadLocal可稱為執行緒區域性變數or本地執行緒變數,該類的作用是為每個執行緒都建立一個變數副本, 每個執行緒都可以修改自己所擁有的變數副本, 而不會影響其他執行緒的副本. 其實這也是解決執行緒安全的問題的一種方法。
2.變數副本是什麼時候“複製”到threadlocal中的呢?這裡“複製”兩個字用的很不專業。準確的說,應該是,變數副本【每個執行緒中儲存的那個map中的變數】是怎麼宣告和初始化的?從原始碼可以看出,當執行緒中的threadlocalmap是null的時候,會呼叫createmap建立一個map。同時根據函式引數設定上初始值。也就是說,當前執行緒的threadlocalmap是在第一次呼叫set的時候建立map並且設定上相應的值的。如果沒呼叫set之前直接呼叫get方法,也會初始化,呼叫的也是createmap方法。下面是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); } /** * 獲取當前執行緒定義的ThreadLocal.ThreadLocalMap threadLocals = null; * @param t * @return */ ThreadLocalMap getMap(Thread t) { return t.threadLocals; } map等於null情況 begin /** * 建立ThreadLocalMap例項物件 * @param t * @param firstValue */ void createMap(Thread t, T firstValue) { // this 指的是 ThreadLocal 例項變數 t.threadLocals = new ThreadLocalMap(this, firstValue); } /** * * @param firstKey * @param firstValue */ ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { table = new Entry[INITIAL_CAPACITY]; // 陣列容量16 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); } private void setThreshold(int len) { threshold = len * 2 / 3; } 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; } } // 陣列是當前執行緒的成員變數ThreadLocal.ThreadLocalMap threadLocals 的成員變數, //用來儲存當前執行緒所有ThreadLocal變數的例項 private Entry[] table; private static final int INITIAL_CAPACITY = 16; // table 陣列初始化容量 private int size = 0; // 當前table存放的個數 private int threshold; // Default to 0 // 何時擴容和此引數有關 len * 2 / 3; map等於null情況 end map不等於null情況 begin private void set(ThreadLocal<?> key, Object value) { 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; } //出現k等於null的情況為,還沒有呼叫set的時候呼叫了get方法, //並將initialValue 初始化的值儲存到陣列中,詳情請看get方法 if (k == null) { replaceStaleEntry(key, value, i); return; } } tab[i] = new Entry(key, value); // 該ThreadLocal物件之前並沒有儲存過 int sz = ++size; // threshold=len * 2 / 3; // 符合條件開始擴容 擴容大小為當前陣列長度*2 if (!cleanSomeSlots(i, sz) && sz >= threshold) rehash(); } //獲取下一個下標 private static int nextIndex(int i, int len) { return ((i + 1 < len) ? i + 1 : 0); } private boolean cleanSomeSlots(int i, int n) { boolean removed = false; Entry[] tab = table; int len = tab.length; do { i = nextIndex(i, len); Entry e = tab[i]; if (e != null && e.get() == null) { n = len; removed = true; i = expungeStaleEntry(i); } } while ( (n >>>= 1) != 0); return removed; } //開始擴容 private void resize() { Entry[] oldTab = table; int oldLen = oldTab.length; int newLen = oldLen * 2; Entry[] newTab = new Entry[newLen]; int count = 0; for (int j = 0; j < oldLen; ++j) { Entry e = oldTab[j]; if (e != null) { ThreadLocal<?> k = e.get(); if (k == null) { e.value = null; // Help the GC } else { int h = k.threadLocalHashCode & (newLen - 1); while (newTab[h] != null) h = nextIndex(h, newLen); newTab[h] = e; count++; } } } setThreshold(newLen); size = count; table = newTab; } map不等於null情況 end
3.每個執行緒的變數副本是儲存在哪裡的?
可以從ThreadLocal的get方法的原始碼中看出來,其中getmap函式是用t作為引數,這裡t就是當前執行的執行緒。從而得知,get函式就是從當前執行緒的threadlocalmap中取出當前執行緒對應的變數的副本【注意,變數是儲存線上程中的,而不是儲存在ThreadLocal變數中】。當前執行緒中,有一個變數引用名字是threadLocals,這個引用是在ThreadLocal類中createmap函式內初始化的。每個執行緒都有一個這樣的threadLocals引用的ThreadLocalMap,以ThreadLocal和ThreadLocal物件宣告的變數型別作為引數。這樣,我們所使用的ThreadLocal變數的實際資料,通過get函式取值的時候,就是通過取出Thread中threadLocals引用的map,然後從這個map中根據當前threadLocal作為引數,取出資料。下面是get方法的原始碼:
public T get() {
Thread t = Thread.currentThread();
//獲取當前執行緒的ThreadLocal.ThreadLocalMap threadLocals 例項物件
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); // 呼叫初始化方法
}
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;
}
/**
* 獲取initialValue初始化的值,並將初始化的值儲存到陣列中去
* @return
*/
private T setInitialValue() {
// 呼叫初始化方法,可以在建立ThreadLocal例項的時候重寫該方法
T value = initialValue();
Thread t = Thread.currentThread();
// 獲取當前執行緒成員變數 ThreadLocal.ThreadLocalMap threadLocals 的例項物件;
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value); // 建立 ThreadLocal.ThreadLocalMap threadLocals 例項物件
return value;
}