1. 程式人生 > >Java併發-深入理解ThreadLocal

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。