淺談ThreadLocal

      ThreadLocal這個名臣帶有必定的迷惑性,千萬不要認爲ThreadLocal是線程的一種實現,網上不少人認爲它應該叫ThreadLocalVariable更貼切,我對此也很是贊同。ThreadLocal存在的意義就是爲了解決線程之間數據數據的衝突,ThreadLocal是線程的局部變量,它裏面的數據只存活在線程的聲明週期之中,並且必須是當前線程才能獲取到對應的數據,其餘的線程不能獲取到當前線程的ThreadLocal中的數據。html

       在對ThreadLocal作了一個簡單的介紹後,下面開始逐一分析。java

1.ThreadLocal是什麼?算法

    ThreadLocal的本質是線程局部變量,能夠理解爲ThreadLocalVariable,它並不是是線程的本地實現,不是一個Thread。多線程

    ThreadLocal的源碼中類聲明以下:併發

public class ThreadLocal<T> {...

2.ThreadLocal的做用?this

       ThreadLocal存在是爲了解決什麼問題的呢?在多線程併發的狀況下,各個線程的在其上下文環境下保存一些變量副本,spa

能夠獨立的改變本身的副本,而不會和其餘線程的副本衝突。線程

       ThreadLocal的數據實際存儲在ThreadLocalMap中,而且以當前的ThreadLocal對象做爲key,當對ThreadLocal作get和set方法的時候,都是以this爲key進行操做的,所以其的ThreadLocal對象獲取當前的ThreadLocal對象的值。code

    setorm

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

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


3.ThreadLocal中的變量存儲在何處?

        ThreadLocal中數據式存儲在ThreadLocal.ThreadLocalMap中的。這個ThreadLocalMap的對象是被Thread對象的threadLocals的屬性引用的。因而可知,ThreadLocal中變量存在Thread對象中,所以在線程的存活期間,若是沒有刻意的去刪除這些屬性,實際上在線程聲明週期中都可以對這些變量進行訪問和操做。

        ThreadLocalMap是做爲Thread類的一個字段。

class Thread implements Runnable {
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }

    private char	name[];
    private int         priority;
    private Thread	threadQ;
    private long	eetop;

    /* Whether or not to single_step this thread. */
    private boolean	single_step;

    /* Whether or not the thread is a daemon thread. */
    private boolean	daemon = false;

    /* JVM state */
    private boolean	stillborn = false;

    /* What will be run. */
    private Runnable target;

    /* The group of this thread */
    private ThreadGroup	group;

    /* The context ClassLoader for this thread */
    private ClassLoader contextClassLoader;

    /* The inherited AccessControlContext of this thread */
    private AccessControlContext inheritedAccessControlContext;

    /* For autonumbering anonymous threads. */
    private static int threadInitNumber;
    private static synchronized int nextThreadNum() {
	return threadInitNumber++;
    }

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
     //請看這裏----
    ThreadLocal.ThreadLocalMap threadLocals = null;

         ThreadLocalMap什麼時候建立,並將對象引用和Thread的threadLocals屬性關聯上呢?在ThreadLocal中有一個createMap的方法,在這個方法裏會建立一個ThreadLocalMap。請看下面代碼:

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

         此時尚未回答什麼時候回建立ThreadLocalMap這個問題。實際上在前面的set方法中已經看到調用createMap的方法了,說明在第一設置數據的時候,若是使用當前ThreadLocal對象沒有找到ThreadLocalMap對象的時候,那麼將會去建立ThreadLocalMap對象,可是起初沒有set就開始調用get方法呢?在get方法裏面調用一個叫setInitialValue的方法。事實上setInitialValue和set方法很是類似,在setInitialValue中也調用createMap方法。請看下面代碼:

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

  至此,我能夠知道了ThreadLocalMap是存儲在Thread對象中,其次TheadLocal會在首次調用get或者set的時候建立ThreadLocalMap對象,而且將這個ThreadLocalMap的對象賦給當前線程的threadlocals屬性。

4.ThreadLocal中數據存儲詳細分析。

    (1)ThreadLocal中數據是存儲在ThreadLocalMap的對象中。而且是ThreadLocalMap對象和Thread對象時一對一的關係。

     (2)一個ThreadLocalMap對象對應的ThreadLocal對象是N個。

      (3)ThreadLocalMap的Entry是一個WeakReference<ThreadLocal>子類,ThreadLocalMap中存儲的時候是以ThreadLocal的對象做爲key,當key釋放了引用(key==null),ThreadLocalMap會馬上移除對應Entry。

        咱們再次看一下ThreadLocalMap.Entry的聲明:

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

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

5.ThreadLocal回收。參考資料來自ThreadLocal內存泄露

被廢棄了的ThreadLocal所綁定對象的引用,會在如下4狀況被清理。

若是此時外部沒有綁定對象的引用,則該綁定對象就能被回收了:

1 Thread結束時。

2 當Thread的ThreadLocalMap的threshold超過最大值時。

3 向Thread的ThreadLocalMap中存放一個ThreadLocal,hash算法沒有命中既有Entry,而須要新建一個Entry時。

4 手工經過ThreadLocal的remove()方法或set(null)。

所以若是咱們粗暴的把ThreadLocal設置null,而不調用remove()方法或set(null),那麼就可能形成ThreadLocal綁定的對象長期也能被回收,於是產出內存泄露。

相關文章
相關標籤/搜索