執行緒區域變數ThreadLocal源碼解讀

來源:互聯網
上載者:User

標籤:first   null   sse   author   nbsp   都對   ble   rgb   class   

一、ThreadLocal基礎知識

    原始線程現狀: 按照傳統經驗,如果某個對象是非安全執行緒的,在多線程環境下,對對象的訪問必須採用synchronized進行線程同步。但是Spring中的各種模板類並未採用線程同步機制,因為線程同步會影響並發性和系統效能,而且實現難度也不小。

ThreadLocal在Spring中發揮著重要的作用。在管理request範圍的bean,交易管理,任務調度,AOP等模組中都出現了它的身影。

ThreadLocal介紹: 它不是一個線程,而是線程的一個本地化對象,或者說是局部變數。當工作於多線程中的對象使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的線程分配一個獨立的變數副本。所以每一個線程都可以獨立地改變自己的副本,而不會影響其他線程所對應的副本,將類變數放到ThreadLocal類型的對象中,使變數在每個線程中都有獨立拷貝,不會出現一個線程讀取變數時而被另一個線程修改的現象

   如何存放執行緒區域變數?

在ThreadLocal類中有一個ThreadLocalMap, 用於存放每一個線程的變數副本,Map中元素的鍵為線程對象,值為對應線程的變數副本。二、源碼解讀

首先說明ThreadLocal存放的值是線程內共用的,線程間互斥的,主要用於線程內共用一些資料,避免通過參數來傳遞,這樣處理後,能夠優雅的解決一些實際問題,比如Hibernate中的OpenSessionInView,就是使用ThreadLocal儲存Session對象,還有我們經常用ThreadLocal存放Connection,代碼如:

  1 1/**  2 * 資料庫連接管理類  3 * @author 爽  4 *  5 */  6 6public class ConnectionManager {  7   8    /** 線程內共用Connection,ThreadLocal通常是全域的,支援泛型 */  9    private static ThreadLocal<Connection> threadLocal = new ThreadLocal<Connection>(); 10  11    public static Connection getCurrConnection() { 12        // 擷取當前線程內共用的Connection   13        Connection conn = threadLocal.get(); 14        try { 15            // 判斷串連是否可用   16            if(conn == null || conn.isClosed()) { 17                // 建立新的Connection賦值給conn(略)   18                // 儲存Connection   19                threadLocal.set(conn); 20            } 21        } catch (SQLException e) { 22            // 異常處理   23        } 24        return conn; 25    } 26  27    /** 28     * 關閉當前資料庫連接 29     */ 30    public static void close() { 31        // 擷取當前線程內共用的Connection   32        Connection conn = threadLocal.get(); 33        try { 34            // 判斷是否已經關閉   35            if(conn != null && !conn.isClosed()) { 36                // 關閉資源   37                conn.close(); 38                // 移除Connection   39                threadLocal.remove(); 40                conn = null; 41            } 42        } catch (SQLException e) { 43            // 異常處理   44        } 45    } 46 }

這樣處理的好處:

1 統一管理Connection;

2 不需要顯示傳參Connection,代碼更優雅;

3 降低耦合性。

       ThreadLocal有四個方法,分別為:

initialValue

protected T initialValue()

返回此線程局部變數的當前線程的初始值。最多在每次訪問線程來獲得每個線程局部變數時調用此方法一次,即線程第一次使用 get() 方法訪問變數的時候。如果線程先於 get 方法調用 set(T) 方法,則不會線上程中再調用 initialValue 方法。

該實現只返回 null;如果程式員希望將線程局部變數初始化為 null 以外的某個值,則必須為 ThreadLocal建立子類,並重寫此方法。通常,將使用匿名內部類。initialValue 的典型實現將調用一個適當的構造方法,並返回新構造的對象。

返回:

返回此線程局部變數的初始值

get

public T get()

返回此線程局部變數的當前線程副本中的值。如果這是線程第一次調用該方法,則建立並初始化此副本。

返回:

此線程局部變數的當前線程的值

set

public void set(T value)

將此線程局部變數的當前線程副本中的值設定為指定值。許多應用程式不需要這項功能,它們只依賴於initialValue() 方法來設定線程局部變數的值。

參數:

value - 儲存在此線程局部變數的當前線程副本中的值。

remove

public void remove()

移除此線程局部變數的值。這可能有助於減少線程局部變數的儲存需求。如果再次訪問此線程局部變數,那麼在預設情況下它將擁有其 initialValue。

       很多人對ThreadLocal存在一定的誤解,說ThreadLocal中有一個全域的Map,set時執行map.put(Thread.currentThread(), value),get和remove時也同理,但SUN的大師們是否是如此實現的,我們只能去看源碼了。

       set方法:

  1 /**  2  * Sets the current thread‘s copy of this thread-local variable  3  * to the specified value.  Most subclasses will have no need to  4  * override this method, relying solely on the {@link #initialValue}  5  * method to set the values of thread-locals.  6  *  7  * @param value the value to be stored in the current thread‘s copy of  8  *        this thread-local.  9  */ 10 public void set(T value) { 11     // 擷取當前線程對象   12     Thread t = Thread.currentThread(); 13     // 擷取當前執行緒區域變數Map   14     ThreadLocalMap map = getMap(t); 15     // map不為空白   16     if (map != null) 17         // 存值   18         map.set(this, value); 19     else 20         // 建立一個當前執行緒區域變數Map   21         createMap(t, value); 22 } 23  24 /** 25  * Get the map associated with a ThreadLocal. Overridden in 26  * InheritableThreadLocal. 27  * 28  * @param  t the current thread 29  * @return the map 30  */ 31 ThreadLocalMap getMap(Thread t) { 32     // 擷取當前線程的本地變數Map   33     return t.threadLocals; 34 }

這裡注意,ThreadLocal中是有一個Map,但這個Map不是我們平時使用的Map,而是ThreadLocalMap,ThreadLocalMap是ThreadLocal的一個內部類,不對外使用的。當使用ThreadLocal存值時,首先是擷取到當前線程對象,然後擷取到當前執行緒區域變數Map,最後將當前使用的ThreadLocal和傳入的值放到Map中,也就是說ThreadLocalMap中存的值是[ThreadLocal對象, 存放的值],這樣做的好處是,每個線程都對應一個本地變數的Map,所以一個線程可以存在多個執行緒區域變數。

get方法:

  1 /**  2  * Returns the value in the current thread‘s copy of this  3  * thread-local variable.  If the variable has no value for the  4  * current thread, it is first initialized to the value returned  5  * by an invocation of the {@link #initialValue} method.  6  *  7  * @return the current thread‘s value of this thread-local  8  */  9 public T get() { 10     Thread t = Thread.currentThread(); 11     ThreadLocalMap map = getMap(t); 12     if (map != null) { 13         ThreadLocalMap.Entry e = map.getEntry(this); 14         if (e != null) 15             return (T)e.value; 16     } 17     // 如果值為空白,則返回初始值   18     return setInitialValue(); 19 }

了之前set方法的分析,get方法也同理,需要說明的是,如果沒有進行過set操作,那從ThreadLocalMap中拿到的值就是null,這時get方法會返回初始值,也就是調用initialValue()方法,ThreadLocal中這個方法預設返回null。當我們有需要第一次get時就能得到一個值時,可以繼承ThreadLocal,並且覆蓋initialValue()方法

優秀博文:

ThreadLocal





內容來自:cnblogs:牛奶、不加糖

執行緒區域變數ThreadLocal源碼解讀

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.