揭祕“ThreadLocal"

引導語

ThreadLocal也是線程安全的一種措施,有這麼一個經典的比喻“人手一隻筆”,如果有100個人要簽名,只有一支筆的話,那麼肯定要排起長隊了,但如果準備了100支筆,那就可以做到每人一支筆啦。

ThreadLocal的官方解釋:線程局部變量,是一個以ThreadLocal對象爲鍵,任意對象爲值得存儲結構,這個結構倍附帶在線程上,也就是說一個線程可以根據一個ThreadLocal對象查詢綁定在這個線程上的一個值。(Java併發編程的藝術,P105)

讓我們結合源碼和示例,來理解ThreadLocal吧!

 

一、ThreadLocal源碼

在Java.lang包下

1、構造方法:只有一個無參構造

public ThreadLocal() {}

2、常用方法:set()

    public void set(T value) {
        Thread t = Thread.currentThread(); //獲得當前線程
        ThreadLocalMap map = getMap(t);    //獲得ThreadLocalMap 
        if (map != null)
            map.set(this, value);          //map的常規賦值操作
        else
            createMap(t, value);
    }
  • 緊接着,我們看這個getMap()方法做了什麼
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;  //根據當前線程,從當前線程中取出ThreadLocalMap
    }
  • 原來存儲着各種變量的map在Thread中!讓我們來到Thread類的源碼中看看:
    ThreadLocal.ThreadLocalMap threadLocals = null;
  • 回到ThreadLocal源碼,再看看createMap()做了什麼:
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }
  • 總結:set方法調用時,首先獲取當前線程,再從當前線程中獲取ThreadLocalMap,如果這個map不爲空,則set值,如果這個map是空的,則在當前線程創建ThreadLocalMap,然後再set。
  • 注意:ThreadLocalMap是屬於線程的,這個map的key是ThreadLocal對象,value就是它在當前線程中的取值。
  • 上圖:圖片來自網絡,侵刪,謝謝!

3、常用方法:get()

    public T get() {
        Thread t = Thread.currentThread();  //先得到當前線程
        ThreadLocalMap map = getMap(t);     //取出當前線程中的ThreadLocalMap 
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this); 
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

 

三、使用示例

  • ThreadLocal一般定義成靜態成員變量,以供使用

1、實現了Runnable接口的類

class ThreadLocalTest implements Runnable{
    static ThreadLocal<String> str=new ThreadLocal<String>(){
        @Override
        protected String initialValue() {
            return Thread.currentThread().getName();  //設置初始值爲線程名
        }
    };

    @Override
    public void run(){
        try {
            //打印出當前線程名,ThreadLocal對象str在當前線程中的值
            System.out.println(Thread.currentThread().getName()+":"+str.get());
            Thread.sleep(10);
        }catch (Exception e){
            System.out.println(e);
        }
    }
}

2、主方法

    public class TryThreadLocal {
        public static void main(String[] args){
            Thread t1=new Thread(new ThreadLocalTest());
            Thread t2=new Thread(new ThreadLocalTest());
            t1.start();
            t2.start();
        }
    }

3、運行結果

 

三、應用場景

1、用在數據庫連接中,用threadlocal保存Connection對象,可以保證在線程各處的數據庫連接都是相同的,而且各個請求之間不影響。

2、用在session管理中,可以保證每個請求的會話不會相互影響。

3、線程上下文相關的信息,例如用戶ID、交易ID等等。

 

 

感謝:

https://my.oschina.net/davidzhang/blog/111010

https://droidyue.com/blog/2016/03/13/learning-threadlocal-in-java/

 

 

 

 

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