首頁  >  文章  >  Java  >  ThreadLocal原理淺析

ThreadLocal原理淺析

(*-*)浩
(*-*)浩轉載
2019-09-29 16:18:302637瀏覽

ThreadLocal即線程局部變數的意思!所以什麼是線程局部變數?這玩意有什麼鳥用?是不是面試被問到了說不出個一二三?今天就來扒一扒這貨的源碼,從根本上了解這貨是乾啥的。

ThreadLocal原理淺析

Thread、ThreadLocalMap、Entry三者關係

其實研究下來他的原始碼實現,其實也沒想像的那麼複雜,其最主要有以下幾點:

1、Java可以透過Thread.currentThread()來獲得目前的Thread的實例物件。既然能拿到這Thread物件實例,那麼我們就可以操作該實例(的屬性),例如為該Thread物件設定一個值什麼。

2、每一個Thread物件都有一個ThradLocalMap實例,該實例有一個Entry組成的數組,Entry物件有兩個主要屬性:value和ThreadLocal的弱引用,其中value這個屬性就是值設定給當前線程所持有,也是ThreadLocal的核心屬性:

static class Entry extends WeakReference<ThreadLocal<?>> {
    /** The value associated with this ThreadLocal. */
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
        super(k);
        value = v;
    }
}

注意Entry繼承自WeakReference,其key就是ThreadLocal物件! (圖1)

ThreadLocal原理淺析

結合1和2兩個知識點,我們就可以知道我們拿到Thread對象之後,就可以操控當前線程對象的ThreadLocalMap對象,然後把想要保存的value交給ThreadLocalMap的Entry的value屬性,Thread,ThreadLocalMap,value三者之間的關係可以用下圖表示(圖2):

ThreadLocal原理淺析

透過上圖我們可以得到這麼一個結論:一個Thread對象持有一個ThreadLocalMap對象,然後呢,一個ThreadLoalMap對象又包含了多個ThreadLlocal對象及ThreadLocal對象所在線程的value! ! !一言以蔽之: 一個Thread物件可以持有多個ThreadLocal物件的變數值value

那麼ThreadLocal和Thread又有啥關係呢?二者是怎能對value進行讀取的呢?下面就根據原始碼來簡單的分析下。

ThreadLocal和Thread的關聯

先看看ThreadLocal的set方法:

 public void set(T value) {
         //获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程持有的ThreadLocalMap
        ThreadLocal.ThreadLocalMap map = getMap(t);
        //将value设置给threadlocalMap
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

set方法很邏輯很簡單(j結合上圖2看更好理解):

1、透過currentThread方法拿到當前Thread物件

2、取得目前Thread物件的ThreadLoalMap物件

3、將value連同ThreadLocal對象自己組成一個Entry物件保存在

ThreadLoalMap的Entry類型的陣列中。

在來看看ThreadLocal的get方法:

public T get() {
    //获取当前线程
    Thread t = Thread.currentThread();
    //获取当前线程的ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        //获取与ThreadLocal对象想关联的value
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            //获取值
            T result = (T)e.value;
            return result;
        }
    }
    //为空返回初始化值
    return setInitialValue();
}

可以發展get的整體邏輯也很簡單:

1、取得目前Thread物件

2、取得目前Thread物件的ThreadLocalMap物件

3、從ThreadLocalMap取得與ThreadLocal相關聯的Entry對象,具體的就是以ThreadLocal為key取得。

4、取得步驟3的Entry的value屬性,並回傳。

透過整體觀察get與set方法可以得到以下結論:ThreadLocal物件呼叫set方法就是在Thread物件的ThreadLocalMap裡面加上值;ThreadLocal物件呼叫get方法就是從Thread物件的ThreadLocalMap裡面取得值。核心就是操縱Thread物件的ThreadLocalMap物件進行value的讀取與寫入。原理就這麼簡單。

那麼位於不同執行緒的不同ThreadLocal對象,在其他執行緒裡保存值是什麼樣的關係呢?可以透過下圖來清楚的描述出來:

ThreadLocal原理淺析

ThreadLocal的使用實例

我們在知道在Android中一個執行緒只有一個Looper對象,那麼怎麼做到的呢?就是ThreadLocal發揮了作用,看看Looper的prepare方法:

//定义一个静态的ThreadLocal变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 

private static void prepare(boolean quitAllowed) {
        //一个Thread只能关联一个Looper对象
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

觀察prepare方法可以知道,先透過sThreadLocal的get方法判斷當前線程是否已經擁有了一個Looper對象,如果有就拋出一個異常;如果當前線程還沒有設定Looper對象,則呼叫ThreadLocal的set方法,初始化一個Looper對象交給當前線程:

sThreadLocal.set(new Looper(quitAllowed));

這樣就確保了一個線程只有一個Looper對象。

到此為止,關於ThreadLocal的原理已經基本分析完畢,至於內部是怎麼set和get的,博主並沒有做太細的分析,因為沒必要,了解ThreadLocal的工作原因以及使用場景即可。

以上是ThreadLocal原理淺析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:csdn.net。如有侵權,請聯絡admin@php.cn刪除