首先舉例說明ThreadLocal類的使用:
public class ThreadTest {
static ThreadLocal<String> tLocal = new ThreadLocal<String>();
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
tLocal.set(Thread.currentThread().getName());//將main線程名稱保存到tlocal中
//使用匿名類創建一個新線程
Thread thread = new Thread(){
public void run(){
tLocal.set(Thread.currentThread().getName());
System.out.println(tLocal.get());
}
};
thread.start();
thread.join();
System.out.println(tLocal.get());
} catch (InterruptedException e) {
// TODO: handle exception
e.printStackTrace();
}
}
}
運行結果:
Thread-0
Main
根據運行結果可知,在threadlocal中爲每個線程存儲一個單獨的值。下面,結合上面例子,分析源碼具體瞭解該類的原理:
首先threadlocal實例化,查看構造函數,沒有任何其它操作。
public ThreadLocal() {
}
Set()方法理解:
public void set(T value) {
//獲取當前線程
Thread t = Thread.currentThread();
//ThreadLocalMap是ThreadLocal的靜態內部類,具體見下面getMap方法,
//第一次執行該方法,map是null,
ThreadLocalMap map = getMap(t);
if (map != null)
//具體見下面set方法
map.set(this, value);
else
//當map爲null是,創建map,具體見下面的createMap源碼
createMap(t, value);
}
//在Thread類中,定義了threadLocals變量
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocalMap.set()方法:
private void set(ThreadLocal key, Object value) {
//獲取類型爲Entry的table數組,Entry是ThreadLocalMap的內部類
Entry[] tab = table;
int len = tab.length;
//根據key的hash值與15做&運算,得出下標
int i = key.threadLocalHashCode & (len-1);
//循環,當e不爲null,執行循環體
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;
}
}
//實例化Entry,賦給tab[i],其實threadlocal本質就是將每個線程中的
//theadlocal.threadlocalmap對象和共享的變量,形成entry對象,
tab[i] = new Entry(key, value);
int sz = ++size;
//當table數組中的元素大於10,擴展table;即每個線程中的threadlocal //數量大於10時,會對table數組擴展
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
ThreadLocal.createMap(Thread t,T firstValue)方法:
//爲每個線程創建ThreadLocalMap實例,
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
//ThreadLocalMap構造函數,傳入
ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {
//創建Entry類型的數組,長度爲16
table = new Entry[INITIAL_CAPACITY];
//獲取存放Entry實例的下標
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
//設置擴展table長度的閾值
setThreshold(INITIAL_CAPACITY);
}
以上分析代碼可知,ThreadLocal在多線程中的應用中的原理如下圖:
根據圖可知,每個線程中,都有自己獨立的了ThreadLocalMap類的實例,ThreadLocalMap中的key是tLocal對象的引用,value表示每個線程中的獨立的變量值,ThreadLocal的set方法的實質就是:若當前線程中ThreadLocalMap爲null,則實例化ThreadLocalMap,同時傳入ThreadLocal的引用和變量值;若不爲null,執行ThreadLocalMap的set方法,同樣需要傳入ThreadLocal的引用和變量值。
當需要獲取變量值時,調用ThreadLocal的get方法,自動獲取當前線程中的ThreadLocalMap對象,並獲取相應的變量值。
ThreadLocal並不是什麼線程,確切地說是一個線程的變量,通過該變量,可以實現多線程之間的安全訪問全局變量。ThreadLocal同synchronized,都是解決線程安全問題,ThreadLocal是通過“空間換取時間”的方式,synchronized是通過“時間換取空間”的方式。