Java併發-剖析ThreadLocal源碼

一、ThreadLocal是什麼?

ThreadLocal與線程同步機制不同,線程同步機制是多個線程共享同一個變量,而ThreadLocal是爲每一個線程創建一個單獨的變量副本,故而每個線程都可以獨立地改變自己所擁有的變量副本,而不會影響其他線程所對應的副本。可以說ThreadLocal爲多線程環境下變量問題提供了另外一種解決思路。

二、ThreadLocal常用方法的源碼

1、set()方法

//set操作,爲線程綁定變量
public void set(T value) {
    Thread t = Thread.currentThread();//1.首先獲取當前線程對象
    ThreadLocalMap map = getMap(t);//2.獲取該線程對象的ThreadLocalMap
    if (map != null)
        map.set(this, value);//如果map不爲空,執行set操作,以當前threadLocal對象爲key,實際存儲對象爲value進行set操作
    else
        createMap(t, value);//如果map爲空,則爲該線程創建ThreadLocalMap
}
   
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;//線程對象持有ThreadLocalMap的引用
}

//線程t.threadLocals定義
ThreadLocal.ThreadLocalMap threadLocals = null;
1. `ThreadLocal`僅僅是個變量訪問的入口;
2. 每一個`Thread對象`都有一個`ThreadLocalMap對象`;

2、get()方法

public T get() {
     Thread t = Thread.currentThread();//1.首先獲取當前線程
     ThreadLocalMap map = getMap(t);//2.獲取線程的map對象
     if (map != null) {//3.如果map不爲空,以threadlocal實例爲key獲取到對應Entry,然後從Entry中取出對象即可。
         ThreadLocalMap.Entry e = map.getEntry(this);
         if (e != null)
             return (T)e.value;
     }
     return setInitialValue();//如果map爲空,也就是第一次沒有調用set直接get(或者調用過set,又調用了remove)時,爲其設定初始值
}

private T setInitialValue() {
    T value = initialValue();//獲取初始值
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

//initialValue方法,默認是null,訪問權限是protected,即允許重寫。
protected T initialValue() {
    return null;
}

三、使用示例

public class SeqCount {

    private static ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
        // 實現initialValue()
        public Integer initialValue() {
            return 0;
        }
    };

    public int nextSeq(){
        threadLocal.set(threadLocal.get() + 1);

        return threadLocal.get();
    }

    public static void main(String[] args) {
        SeqCount seqCount = new SeqCount();

		 //每個線程裏map的key都一樣(threadLocal實例爲同一個)
        SeqThread thread1 = new SeqThread(seqCount);
        SeqThread thread2 = new SeqThread(seqCount);
        SeqThread thread3 = new SeqThread(seqCount);
        SeqThread thread4 = new SeqThread(seqCount);

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }

    private static class SeqThread extends Thread {
        private SeqCount seqCount;

        SeqThread(SeqCount seqCount) {
            this.seqCount = seqCount;
        }

        public void run() {
            for(int i = 0 ; i < 3 ; i++){
                System.out.println(Thread.currentThread().getName() + " seqCount :" + seqCount.nextSeq());
            }
        }
    }
}

//執行結果
Thread-0 seqCount :1
Thread-0 seqCount :2
Thread-0 seqCount :3
Thread-2 seqCount :1
Thread-2 seqCount :2
Thread-2 seqCount :3
Thread-1 seqCount :1
Thread-1 seqCount :2
Thread-1 seqCount :3
Thread-3 seqCount :1
Thread-3 seqCount :2
Thread-3 seqCount :3

四、Thread、ThreadLocal、ThreadLocalMap關係圖

這裏寫圖片描述

每個thread中都存在一個map,map的類型是ThreadLocal.ThreadLocalMap。Map中的key爲一個threadlocal實例(引用)。

五、應用場景

ThreadLocal在spring的事務管理,包括Hibernate的session管理等都有出現,在web開發中,有時會用來管理用戶會話 HttpSession,web交互中這種典型的一請求一線程的場景比較適合使用ThreadLocal。

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