2025-02-10

java.lang.ThreadLocal類研究


1、概述
ThreadLocal是什麼呢?其實ThreadLocal並非是一個線程的本地實現版本,它並不是一個Thread,而是thread local variable(線程局部變量)。也許把它命名為ThreadLocalVar更加合適。線程局部變量(ThreadLocal)其實的功用非常簡單,就是為每一個使用該變量的線程都提供一個變量值的副本,是Java中一種較為特殊的線程綁定機制,是每一個線程都可以獨立地改變自己的副本,而不會和其它線程的副本沖突。


從線程的角度看,每個線程都保持一個對其線程局部變量副本的隱式引用,隻要線程是活動的並且 ThreadLocal 實例是可訪問的;在線程消失之後,其線程局部實例的所有副本都會被垃圾回收(除非存在對這些副本的其他引用)。


通過ThreadLocal存取的數據,總是與當前線程相關,也就是說,JVM 為每個運行的線程,綁定瞭私有的本地實例存取空間,從而為多線程環境常出現的並發訪問問題提供瞭一種隔離機制。


ThreadLocal是如何做到為每一個線程維護變量的副本的呢?其實實現的思路很簡單,在ThreadLocal類中有一個Map,用於存儲每一個線程的變量的副本。


概括起來說,對於多線程資源共享的問題,同步機制采用瞭"以時間換空間"的方式,而ThreadLocal采用瞭"以空間換時間"的方式。前者僅提供一份變量,讓不同的線程排隊訪問,而後者為每一個線程都提供瞭一份變量,因此可以同時訪問而互不影響。


2、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方法,以給定一個特定的初始值。


3、示例
view sourceprint?01 public class ThreadLocalTest { 

02     private static ThreadLocal<String> val = new ThreadLocal<String>(); 

03       

04     public static class Thread1 extends Thread { 

05         private String name; 

06         public Thread1(String name) { 

07             this.name = name; 

08         } 

09         public void run() { 

10             System.out.println(name + "初始值:" + val.get()); 

11             val.set("v[" + name + "]"); 

12             System.out.println(name + "設置後值:" + val.get()); 

13         } 

14     } 

15       

16     public static class Thread2 extends Thread { 

17         private String name; 

18         public Thread2(String name) { 

19             this.name = name; 

20         } 

21         public void run() { 

22             System.out.println(name + "初始值:" + val.get()); 

23             val.set("v[" + name + "]"); 

24             System.out.println(name + "設置後值:" + val.get()); 

25         } 

26     } 

27   

28     public static void main(String[] args) { 

29         val.set("123"); 

30         System.out.println("主程序中設置值:" + val.get()); 

31           

32         (new Thread1("A1")).start(); 

33         (new Thread1("A2")).start(); 

34         (new Thread2("B1")).start(); 

35     } 

36 }

 


運行結果:
主程序中設置值:123
A1初始值:null
A1設置後值:v[A1]
B1初始值:null
A2初始值:null
B1設置後值:v[B1]
A2設置後值:v[A2]


4、總結
ThreadLocal使用場合主要解決多線程中數據數據因並發產生不一致問題。ThreadLocal為每個線程的中並發訪問的數據提供一個副本,通過訪問副本來運行業務,這樣的結果是耗費瞭內存,單大大減少瞭線程同步所帶來性能消耗,也減少瞭線程並發控制的復雜度。


ThreadLocal不能使用原子類型,隻能使用Object類型。ThreadLocal的使用比synchronized要簡單得多。


ThreadLocal和Synchonized都用於解決多線程並發訪問。但是ThreadLocal與synchronized有本質的區別。synchronized是利用鎖的機制,使變量或代碼塊在某一時該隻能被一個線程訪問。而ThreadLocal為每一個線程都提供瞭變量的副本,使得每個線程在某一時間訪問到的並不是同一個對象,這樣就隔離瞭多個線程對數據的數據共享。而Synchronized卻正好相反,它用於在多個線程間通信時能夠獲得數據共享。


Synchronized用於線程間的數據共享,而ThreadLocal則用於線程間的數據隔離。


當然ThreadLocal並不能替代synchronized,它們處理不同的問題域。Synchronized用於實現同步機制,比ThreadLocal更加復雜。

作者“NullOn的博客”
 

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *