ThreadLocal的基本原理與實現

1、概念

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

2、基本原理

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

  在具體分析以前,先作幾點說明:函數

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

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

  

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

  一、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()函數:code

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值了。blog

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

 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數組,咱們根據鍵值線程對象,來找到對應線程的變量副本。

相關文章
相關標籤/搜索