ThreadLocal的基本原理與實現


一、概念

  首先,ThreadLocal並不是一個Thread,這個類提供了線程局部變量,這些變量不同於它們的普通對應物,因為訪問某個變量的每個線程都有自己的局部變量,它獨立於變量的初始化副本。

二、基本原理

  ThreadLocal是如何做到為每一線程維護變量的副本的呢?下面通過源碼(jdk1.7版本)來闡述ThreadLocal的基本原理。

  在具體分析之前,先做幾點說明:

  1:在TreadLocal中有一個靜態內部類ThreadLocalMap

static class ThreadLocalMap 
{
    static class Entry extends WeakReference<ThreadLocal> 
    {
            /** The value associated with this ThreadLocal. */
            Object value;
            Entry(ThreadLocal k, Object v)
     {
                super(k);
                value = v;
       }
     }   
    ...
}

  2:ThreadLocal中又定義一個鍵值對Entry,它用ThreadLocal作為鍵值。我們看到在Thread類中有一個ThreadLocalMap的類型的變量叫做threadLocals。

  

  下面具體分析一下ThreadLocal的兩個關鍵函數get()和set():

  1、get()方法

  public T get()
{ Thread t
= Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }

  在get函數中,首先獲取到當前的線程t,再根據t獲取ThreadLocalMap。下面試getMap()函數:

ThreadLocalMap getMap(Thread t) 
{
return t.threadLocals; }

  可以看到,該函數返回就是我們上面提到的每個線程都有的ThreadLocalMap類型變量threadLocals。

  如果map不為空,則根據map.getEntry(this)獲取Entry鍵值對。注意:這里的this指的是當前的ThreadLocal對象,一個Thread可能對應不止一個ThreadLocal,想要知道具體是Thread對應的哪個ThreadLocal,就要在Thread中維護一個ThreadLocalMap,以ThreadLocal為鍵,就可以找到Thread在某個ThreadLocal里對應的本地數據。獲取到Entry后,我們就可以拿到保存在Entry里面的value值了。

private Entry getEntry(ThreadLocal key) 
{
int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }

  如果map為空,則調用setInitialValue()函數進行初始化。並返回initialValue函數返回的值,不覆寫initialValue的情況下,返回的是null。

 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; }

  2、set()方法

    public void set(T value) 
    {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

  set函數同樣是先獲取ThreadLocalMap類型的變量map。

  如果map不為空,則:

 private void set(ThreadLocal key, Object value)
 {
            Entry[] tab = table;
            int len = tab.length;
            int i = key.threadLocalHashCode & (len-1);
            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;
                }
            }
            tab[i] = new Entry(key, value);
            int sz = ++size;
            if (!cleanSomeSlots(i, sz) && sz >= threshold)
                rehash();
        }

  如果map為空,則

void createMap(Thread t, T firstValue) 
{ t.threadLocals
= new ThreadLocalMap(this, firstValue); }

三、總結

  ThreadLocal是通過下面的方式來實現為每一個線程維護變量的副本的:

  在ThreadLocal類中定義了一個ThreadLocalMap,每一個Thread都有一個ThreadLocalMap類型的變量threadLocals,就是用threadLocals來存儲每一個線程的變量副本,threadLocals內部有一個Entry數組,我們根據鍵值線程對象,來找到對應線程的變量副本。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM