ThreadLocal<T> Java線程局部變數
在 IBM XML,FORTRAN 等語言中在文法層面就提供了線程局部變數,但是Java在文法層面並沒有提供這樣的支援,而是在 JDK 1.2 開始就提供了 java.lang.ThreadLocal (並在JDK1.5開始支援泛型 ThreadLoca<T> )用於支援線程局部變數 ,使用ThreadLocal 工具類可以很簡潔地編寫多線程層程式,而不用像傳統的方法那樣編寫大量的 Thread 同步塊;
TheadLocal 不是一個線程,而是儲存執行緒區域化對象的容器,當運行於多線程環境的某個對象使用 ThreadLocal 維護變數時,ThreadLocal 為每個使用該白能量的線程分配一個變數副本,每一個線程可以獨立地改變自己的副本,而不會影響其他線程的副本;
ThreadLocal 提供的介面方法
ThreadLocal 包括以下4個介面,在實現一個線程局部變數類時可以實現以下的部分介面方法:
| void set(Object value) |
設定當前線程的局部變數的值 |
| public Object get() |
返回當前線程的局部變數的值 |
| public void remove() |
噹噹前局部變數刪除時執行的方法,目的是為了減少記憶體佔用; |
| public Object initialValue() |
返回當前線程的局部變數的初始值; |
一個 ThreadLocal 執行個體
以下樣本用過 ThradLocal 實現局部變數:
public class SequenceNumber { //線程局部變數,匿名實現 initValue() 方法 privatestatic ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>(){ public Integer initialValue(){ return 0; } }; publicint getNextNum(){ seqNum.set(seqNum.get() + 1); return seqNum.get(); } publicstatic void main(String[] args){ SequenceNumber sn = new SequenceNumber(); TestClient t1 = new TestClient(sn); TestClient t2 = new TestClient(sn); TestClient t3 = new TestClient(sn); t1.start(); t2.start(); t3.start(); } publicstatic class TestClient extends Thread{ private SequenceNumber sn; public TestClient(SequenceNumber sn){ this.sn = sn; } public void run(){ for(int i=0;i<3;i++) System.out.println("[Thread-"+Thread.currentThread().getName()+"]sn [" + sn.getNextNum() + "] "); } }}
執行的結果如下,可以發現每個線程產生的序號雖然共用同一個 SequenceNumber 執行個體,但是它們並沒有相互幹擾,而是各自產生獨立的序號;
[Thread-Thread-0] sn [1][Thread-Thread-0] sn [2][Thread-Thread-0] sn [3][Thread-Thread-2] sn [1][Thread-Thread-2] sn [2][Thread-Thread-2] sn [3][Thread-Thread-1] sn [1][Thread-Thread-1] sn [2][Thread-Thread-1] sn [3]
ThreadLocal 和Thread 同步機制的比較
對於多線程的資源同步問題,傳統的 Thread 同步機制 和ThreadLocal 所採用的策略是不同的;
Thread 同步機制採用 “時間換空間” 的方式,實行訪問序列化、對象共用化,多個線程共用一個對象,以線程競爭的方式爭奪對象資源;
ThreadLocal 採用 “空間換時間” 的方式,實行訪問並行化、對象獨佔化,為每一個線程提供一份變數拷貝,可以同時訪問而互不影響;