0、ThreadLocal,線程局部變量,或叫線程本地變量均可
1、一般共享變量都是所有線程共享的,而線程局部變量,名字也很貼切,就是它作用域只在線程內被訪問,算是解決共享變量併發問題的一種思路,不修改原來的共享變量,而是自己複製了一份,自己用
2、目前已經知道解決併發問題的三種思路
a、CAS方式
b、鎖方式
c、線程局部變量方式
3、ThreadLocal裏面巧妙的利用了Thread下的一個哈希表,裏面有Entry,其中key就是當前的ThreadLocal,value就是保存的線程局部變量對象,更巧妙的是這個哈希表是在ThreadLocal下創建的,艹,牛逼
4、從new一個ThreadLocal開始
public ThreadLocal() {
}
構造方法很清爽,就是new了一個對象而已
5、我們再去調用ThreadLocal的get()方法,ThreadLocal是泛型類,所以你new的時候,要指定一個類型
public T get() {
Thread t = Thread.currentThread(); //當前線程對象
ThreadLocalMap map = getMap(t); //往下看第6個步驟
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); //往下看第8個步驟
}
a、如果沒有通過ThreadLocal設置過初始對象,則get()方法返回initialValue()方法返回的對象
b、所以你經常看到繼承了ThreadLocal後,重寫了initialValue方法
c、如果你通過ThreadLocal設置過初始對象,則get()方法返回你設置的對象
6、getMap()方法我們是訪問不了的,default權限
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; //請看7號步驟
}
我們看到它返回一個thread對象下的實例變量,叫做threadLocals,接下來我們看看threadLocals的類型
7、位於Thread類下
ThreadLocal.ThreadLocalMap threadLocals = null;
它的類型是ThreadLocalMap,ThreadLocalMap是ThreadLocal下的一個靜態內部類
threadLocals在Thread下只在一個叫做exit()方法下做了賦值操作(不算初始化的null)
private void exit() {
//省略了很多代碼……
threadLocals = null; //看我
//省略了很多代碼……
}
8、那麼threadLocals在哪裏賦值的呢???彆着急,我們繼續看代碼,因爲上面的map爲null,所以走到了這裏
private T setInitialValue() {
T value = initialValue(); //請看9號步驟
Thread t = Thread.currentThread(); //當前線程對象
ThreadLocalMap map = getMap(t); //又一次調用getMap()方法,請看6號步驟
if (map != null)
map.set(this, value); //不爲null,爲哈希表設置元素
else
createMap(t, value); //將當前thread、存儲的第一個對象,傳進去,請看10號步驟
return value;
}
9、initialValue方法也是繼承ThreadLocal時經常要重寫的一個方法,返回一個ThrealLocal存儲的第一個對象
protected T initialValue() {
return null;
}
它的默認實現是返回null
10、開心不開心,我們終於找到了爲t.threadLocals賦值對象的地方,牛逼
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue); //new了一個ThreadLocalMap對象,把當前ThreadLocal對象與第一個要存儲的對象傳了進去,我們去看看ThreadLocalMap的構造方法
}
11、沒錯,一個自定義的哈希表出來了,這邊將key與value傳進來,要構造哈希表的第一個Entry了
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY]; //初始16個容量的數組,底層數組,元素限定爲了Entry,Entry是ThreadLocalMap下的靜態內部類,牛逼
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); //計算哈希地址,請看第12號
table[i] = new Entry(firstKey, firstValue); //安心將Entry放入對應的哈希地址中,key和value隨着Entry存儲起來了
size = 1; //哈希表元素總數加1
setThreshold(INITIAL_CAPACITY); //設置閾值,把默認長度傳進去,請看13號
}
12、先去ThreadLocal下去看下threadLocalHashCode的值
private final int threadLocalHashCode = nextHashCode();
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}
private static AtomicInteger nextHashCode =
new AtomicInteger(); //用了線程安全的原子類,AtomicInteger,默認值是0
private static final int HASH_INCREMENT = 0x61c88647; //增量值用的牛逼
增量值與16-1做了一個位與運算,這樣拿到哈希地址,歐耶
13、看下閾值是:16 * 2 / 3, 即 32 / 3 = 10,閾值是10
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
14、借了一張圖,還不錯
ThreadLocal1代表一個對象
ThreadLocal2也代表一個對象
一個ThreadLocal對象只能存放一個線程局部變量,你可以創建多個ThreadLocal,那樣就可以爲一個線程保存多個線程局部變量了
protected Object initialValue()返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是爲了讓子類覆蓋而設計的,哈哈,艹