線程內部存儲---TheadLocal從使用到源碼分析

ThreadLocal是什麼?

ThreadLocalThreadLocal是一個線程內部用於存儲數據的類,通過它可以在指定的線程中存儲數據,數據存儲以後,只有在該線程中可以獲取到存儲的數據,對於其它線程來說無法獲取到數據。個人認爲是一個線程內部的存儲機制。

如何使用?


ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();

這樣就在一個線程創建了ThreadLocal這個對象,這個對象支持範性的,也就是說我們可以存儲的自己想存的任意類型。


threadLocal.set(true);

這樣我們就在當前所在線程,存儲了true。


new Thread("Thread#1") {
@Override
public void run() {
System.out.println(threadLocal.get());
};
}.start();

我們在一個分線程去打印這個剛纔存儲的值會發現是null,因爲這是在兩個線程操作的。如果在當前線程獲取則爲正確剛纔存的值。
如果想移除這個數據也很簡單:

threadLocal.remove();

這就是threadLocal的使用,其實很簡單。 主要記得是區分線程的就ok。

內部源碼是如何實現的呢?

image.png
這就是treadLocal中的所有方法了,其實我們最關心的就是set和get方法。

我們先看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);
}

這就是set方法,對就這麼幾行。
首先會通過Thread.currentThread();這個方法獲取當前線程的Thread。然後通過getMap()獲取 ThreadLocalMap對象。

ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}

getMap中也很簡單,就是返回這個線程的threadLocals, 這個ThreadLocal就是 Thread中的一個變量ThreadLocal.ThreadLocalMap threadLocals = null;
其實還是ThreadLocal中的內部類ThreadLocalMap;這裏一會在分析,先順着思路往下走。
獲取map之後會根據判斷如果不是null就進行set,也就是存儲,如果是null就會調用createMap()方法進行創建這個ThreadMap。接下來在看是如何set和createMap的。

創建其實很簡單,直接就是new一個。

void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

set方法就稍微複雜了些,因爲這也是核心內容。set方法在內部類ThreadLocalMap中,所以接下來分析下這個ThreadLocalMap類。

ThreadLocalMap

這個類在構造中創建了一個數組, new Entry[INITIAL_CAPACITY]; ,Entry裏面就是一個object的對象,然後裏面主要getEntry和set方法進行存取和讀取。

““
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) {//這裏判斷這個k和傳進來的key是否相等
                e.value = value;//進行存儲後return
                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();
    }

““
看主要代碼 判斷k和傳進來的key相等話就存儲這個value
get方法:

“`
private Entry getEntry(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();
}

get裏面也是要先獲取當前的Thread這也就是爲什麼ThreadLocal獲取和存儲的都是隻在當前線程的。

然後也是getMap方法獲取這個ThreadLocalMap,這也就是爲什麼裏面就一行代碼,也要寫成一個方來,因爲這是中思路,其他地方獲取直接調用就行,日後擴展的話一樣方便。

再然後就是通過這個map去調用上面說的getEntry方法。

至此,我們就知道ThreadLocal的總體工作流程和思路了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章