ThreadLocal的基本原理與實現

1、概念

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

2、基本原理

  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。this

  

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

  一、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。htm

  若是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。blog

複製代碼
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;
 }
複製代碼

  二、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);
}

3、總結

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

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

相關文章
相關標籤/搜索