1. 程式人生 > >ThreadLocal類的使用,ThreadLocal和synchonzied同步機制相比

ThreadLocal類的使用,ThreadLocal和synchonzied同步機制相比

ThreadLocal是解決執行緒安全問題一個很好的思路,ThreadLocal類中有一個Map,用於儲存每一個執行緒的變數副本,Map中元素的鍵為執行緒物件,而值對應執行緒的變數副本,由於Key值不可重複,每一個“執行緒物件”對應執行緒的“變數副本”,而到達了執行緒安全。

我們知道Spring通過各種DAO模板類降低了開發者使用各種資料持久技術的難度。這些模板類都是執行緒安全的,也就是說,多個DAO可以複用同一個模板例項而不會發生衝突。

我們使用模板類訪問底層資料,根據持久化技術的不同,模板類需要繫結資料連線或會話的資源。但這些資源本身是非執行緒安全的,也就是說它們不能在同一時刻被多個執行緒共享。

雖然模板類通過資源池獲取資料連線或會話,但資源池本身解決的是資料連線或會話的快取問題,並非資料連線或會話的執行緒安全問題。

按照傳統經驗,如果某個物件是非執行緒安全的,在多執行緒環境下,對物件的訪問必須採用synchronized進行執行緒同步。但SpringDAO模板類並未採用執行緒同步機制,因為執行緒同步限制了併發訪問,會帶來很大的效能損失。

此外,通過程式碼同步解決效能安全問題挑戰性很大,可能會增強好幾倍的實現難度。那模板類究竟仰丈何種魔法神功,可以在無需同步的情況下就化解執行緒安全的難題呢?答案就是ThreadLocal

ThreadLocalSpring中發揮著重要的作用,在管理

request作用域的Bean、事務管理、任務排程、AOP等模組都出現了它們的身影,起著舉足輕重的作用。要想了解Spring事務管理的底層技術,ThreadLocal是必須攻克的山頭堡壘。

ThreadLocal是什麼

早在JDK 1.2的版本中就提供java.lang.ThreadLocalThreadLocal為解決多執行緒程式的併發問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優美的多執行緒程式。

ThreadLocal很容易讓人望文生義,想當然地認為是一個本地執行緒。其實,ThreadLocal並不是一個Thread,而是Thread的區域性變數,也許把它命名為ThreadLocalVariable

更容易讓人理解一些。

當使用ThreadLocal維護變數時,ThreadLocal為每個使用該變數的執行緒提供獨立的變數副本,所以每一個執行緒都可以獨立地改變自己的副本,而不會影響其它執行緒所對應的副本。

從執行緒的角度看,目標變數就象是執行緒的本地變數,這也是類名中“Local”所要表達的意思。

執行緒區域性變數並不是Java的新發明,很多語言(如IBM IBM XL FORTRAN)在語法層面就提供執行緒區域性變數。在Java中沒有提供在語言級支援,而是變相地通過ThreadLocal的類提供支援。

所以,在Java中編寫執行緒區域性變數的程式碼相對來說要笨拙一些,因此造成執行緒區域性變數沒有在Java開發者中得到很好的普及。

總結起來就是:

1、每個執行緒都有自己的區域性變數

    每個執行緒都有一個獨立於其他執行緒的上下文來儲存這個變數,一個執行緒的本地變數對其他執行緒是不可見的

2、獨立於變數的初始化副本

    ThreadLocal可以給一個初始值,而每個執行緒都會獲得這個初始化值的一個副本,這樣才能保證不同的執行緒都有一份拷貝。

3、狀態與某一個執行緒相關聯

    ThreadLocal 不是用於解決共享變數的問題的,不是為了協調執行緒同步而存在,而是為了方便每個執行緒處理自己的狀態而引入的一個機制,理解這點對正確使用ThreadLocal至關重要。

ThreadLocal使用的一般步驟 1、在多執行緒的類(如ThreadDemo類)中,建立一個ThreadLocal物件threadXxx,用來儲存執行緒間需要隔離處理的物件xxx。 2、在ThreadDemo類中,建立一個獲取要隔離訪問的資料的方法getXxx(),在方法中判斷,若ThreadLocal物件為null時候,應該new()一個隔離訪問型別的物件,並強制轉換為要應用的型別。 3、在ThreadDemo類的run()方法中,通過getXxx()方法獲取要操作的資料,這樣可以保證每個執行緒對應一個數據物件,在任何時刻都操作的是這個物件。

ThreadLocal和synchonzied同步機制相比

1.synchonzied同步機制是為了實現同步多執行緒對相同資源的併發訪問控制。同步的主要目的是保證多執行緒間的資料共享。同步會帶來巨大的效能開銷,所以同步操作應該是細粒度的(物件中的不同元素使用不同的鎖,而不是整個物件一個鎖)。如果同步使用得當,帶來的效能開銷是微不足道的。使用同步真正的風險是複雜性和可能破壞資源安全,而不是效能。 

2.ThreadLocal以空間換取時間,提供了一種非常簡便的多執行緒實現方式。因為多個執行緒併發訪問無需進行等待,所以使用ThreadLocal會獲得更大的效能。

3.ThreadLocal中的物件,通常都是比較小的物件。另外使用ThreadLocal不能使用原子型別,只能使用Object型別。ThreadLocal的使用比synchronized要簡單得多。 

4.synchronized是利用鎖的機制,使變數或程式碼塊在某一時該只能被一個執行緒訪問。而ThreadLocal為每一個執行緒都提供了變數的副本,使得每個執行緒在某一時間訪問到的並不是同一個物件,這樣就隔離了多個執行緒對資料的資料共享。而Synchronized卻正好相反,它用於在多個執行緒間通訊時能夠獲得資料共享。 

5.Synchronized用於執行緒間的資料共享,而ThreadLocal則用於執行緒間的資料隔離。

常用API:
ThreadLocal()           建立一個執行緒本地變數。 T get()           返回此執行緒區域性變數的當前執行緒副本中的值,如果這是執行緒第一次呼叫該方法,則建立並初始化此副本。 protected  T initialValue()           返回此執行緒區域性變數的當前執行緒的初始值。最多在每次訪問執行緒來獲得每個執行緒區域性變數時呼叫此方法一次,即執行緒第一次使用 get() 方法訪問變數的時候。如果執行緒先於 get 方法呼叫 set(T) 方法,則不會線上程中再呼叫 initialValue 方法。    若該實現只返回 null;如果程式設計師希望將執行緒區域性變數初始化為 null 以外的某個值,則必須為 ThreadLocal 建立子類,並重寫此方法。通常,將使用匿名內部類。initialValue 的典型實現將呼叫一個適當的構造方法,並返回新構造的物件。 void remove()           移除此執行緒區域性變數的值。這可能有助於減少執行緒區域性變數的儲存需求。如果再次訪問此執行緒區域性變數,那麼在預設情況下它將擁有其 initialValue。 void set(T value)           將此執行緒區域性變數的當前執行緒副本中的值設定為指定值。許多應用程式不需要這項功能,它們只依賴於 initialValue() 方法來設定執行緒區域性變數的值。 在程式中一般都重寫initialValue方法,以給定一個特定的初始值。
參考:

http://blog.csdn.net/lufeng20/article/details/24314381

http://justsee.iteye.com/blog/791919

http://ifeve.com/concurrenthashmap/