ThreadLocal理解

首先舉例說明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在多線程中的應用中的原理如下圖:
threadlocal原理圖
根據圖可知,每個線程中,都有自己獨立的了ThreadLocalMap類的實例,ThreadLocalMap中的key是tLocal對象的引用,value表示每個線程中的獨立的變量值,ThreadLocal的set方法的實質就是:若當前線程中ThreadLocalMap爲null,則實例化ThreadLocalMap,同時傳入ThreadLocal的引用和變量值;若不爲null,執行ThreadLocalMap的set方法,同樣需要傳入ThreadLocal的引用和變量值。

當需要獲取變量值時,調用ThreadLocal的get方法,自動獲取當前線程中的ThreadLocalMap對象,並獲取相應的變量值。

ThreadLocal並不是什麼線程,確切地說是一個線程的變量,通過該變量,可以實現多線程之間的安全訪問全局變量。ThreadLocal同synchronized,都是解決線程安全問題,ThreadLocal是通過“空間換取時間”的方式,synchronized是通過“時間換取空間”的方式。

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