深入理解線程本地變量ThreadLocal

fengjun 7年前發布 | 12K 次閱讀 線程 Java開發 ThreadLocal

ThreadLocal理解:

如果在多線程并發環境中,一個可變對象涉及到共享與競爭,那么該可變對象就一定會涉及到線程間同步操作,這是多線程并發問題。

否則該可變對象將作為線程私有對象,可通過ThreadLocal進行管理,實現線程間私有對象隔離的目的。

可以發現,ThreadLocal并沒有解決多線程并發的問題,因為ThreadLocal管理的可變對象的性質本來就不會涉及到多線程并發而引發的共享、競爭和同步問題,使用ThreadLocal管理只是方便了多線程獲取和使用該私有可變對象的途徑和方式。

ThreadLocal解決的問題:

將一個公共的可變對象,轉換為線程私有的可變對象,防止出現不正確的共享。

《java并發變成實踐》

線程本地(ThreadLocal)變量通常用于防止在基于可變的單體(Singleton)或全局變量的設計中,出現(不正確的)共享。

ThreadLocal的使用場景:

一個可變對象,在每一個線程(或者說多線程環境)中都會被使用,并且,該可變對象無需在各個線程間進行同步,那么,該可變對象就可以通過ThreadLocal進行管理,而此時該可變對象變成多實例對象,與線程一一對應。

ThreadLocal主要方法:

  • public T get() { }返回當前線程所對應的線程局部變量

  • public void set(T value) { }設置當前線程的線程局部變量的值

  • public void remove(){} 將當前線程局部變量的值刪除,目的是為了減少內存的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束后,對應該線程的局部變量將自動被垃圾回收,所以顯式調用該方法清除線程的局部變量并不是必須的操作,但它可以加快內存回收的速度。

  • protected T initialValue() { }返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的。這個方法是一個延遲調用方法,在線程第1次調用get()或set(Object)時才執行,并且僅執行1次。ThreadLocal中的缺省實現直接返回一個null。

ThreadLocal源碼分析:

get方法:

/**

 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
public T get() {
//獲取當前線程對象
    Thread t = Thread.currentThread();

//從當前線程對象中獲取ThreadLocalMap類型實例對象
    ThreadLocalMap map = getMap(t);

    if (map != null) {

    //this對象,也就是ThreadLocal對象作為map的key,獲取ThreadLocal管理的可變對象
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }

//map為空,延遲執行初始化方法
    return setInitialValue();
}</code></pre> 

看一下getMap(t)方法:

/**
     * Get the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param  t the current thread
     * @return the map
     */
    ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }

補充解釋:

1、ThreadLocalMap,是ThreadLocal的靜態內部類

2、ThreadLocalMap把其外部類ThreadLocal的實例對象作為key,把要管理的可變對象作為value

3、ThreadLocalMap的實例對象,由當前線程對象Thread的實例持有,而不是由ThreadLocal持有

如下圖源碼截圖進行證明:

然后繼續看setInitialValue()方法和initialValue()方法:

/**
     * Variant of set() to establish initialValue. Used instead
     * of set() in case user has overridden the set() method.
     *
     * @return the initial value
     */
    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;
    }
protected T initialValue() {
        return null;
    }

補充說明:

1、initialValue()方法聲明為protected,目的就是讓子類覆蓋重新的,如果不覆蓋重寫,則返回null

2、如果沒有重寫initialValue()方法,ThreadLocal對象直接調用get()方法,最終從setInitialValue()返回的對象為null

看一下createMap(t, value)方法:

/**
     * Create the map associated with a ThreadLocal. Overridden in
     * InheritableThreadLocal.
     *
     * @param t the current thread
     * @param firstValue value for the initial entry of the map
     */
    void createMap(Thread t, T firstValue) {
    //直接new一個新的ThreadLocalMap實例,封裝進入當前線程對象t
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

從上面代碼可以發現,每一個線程對象都有一個專屬自己的ThreadLocalMap對象,而ThreadLocalMap對象存儲了ThreadLocal對象與變量對象。線程對象、ThreadLocalMap對象、ThreadLocal對象、變量對象之間的關系如下圖:

最后看一下set(T value)方法:

/**
     * Sets the current thread's copy of this thread-local variable
     * to the specified value.  Most subclasses will have no need to
     * override this method, relying solely on the {@link #initialValue}
     * method to set the values of thread-locals.
     *
     * @param value the value to be stored in the current thread's copy of
     *        this thread-local.
     */
    public void set(T value) {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
    }

補充解釋:

1、set方法用來修改或者初始化ThreadLocal管理的變量對象。

2、ThreadLocal對象調用get方法獲取變量的,要么重寫initialValue()方法,要么主動調用set方法,否則將返回null。

參考:

http://blog.csdn.net/lufeng20/article/details/24314381 (此文爭議比較大,但依然可以作為理解ThreadLocal的素材)

http://www.cnblogs.com/dolphin0520/p/3920407.html http://www.iteye.com/topic/103804

 

來自:http://blog.csdn.net/javaloveiphone/article/details/58600694

 

 本文由用戶 fengjun 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!