官方對ThreadLocal的描述:
"該類提供了線程局部(thread-local)變量。這些變量不同於它們的普通對應物,因為訪問某個變量(通過其get或set方法)的每個線程都有自己的局部變量,它獨立於變量的初始化副本。ThreadLocal實例通常是類中的private static字段,它們希望將狀態與某一個線程(例如,用戶ID或事物ID)相關聯。"
《Thinking in Java》中的描述:
防止任務在共享資源上產生沖突的第二種方式是根除對變量的共享。線程本地存儲是一種自動化機制,可以為使用變量的每個不同的線程都創建不同的存儲。因此,如果你有5個線程都要使用變量x所表示的對象,那線程本地存儲就會生成5個用於x的不同的存儲塊。主要是,它們使得你可以將狀態與線程關聯起來。
三個要點:
1.每個線程都有自己的局部變量
每個線程都有一個獨立於其它線程的上下文來保存這個變量,一個線程的本地變量對其它線程是不可見的。
2.獨立於變量的初始化副本
ThreadLocal可以給一個初始值,而每個線程都會獲得這個初始化值的一個副本,這樣才能保證不同的線程都有一份拷貝。
3.狀態與某一個線程相關聯
ThreadLocal不是用於解決共享變量的問題的,不是為了協調線程同步而存在,而是為了方便每個線程處理自己的狀態而引入的一個機制,就像《Thinking in Java》中描述的那樣:”它們使得你可以將狀態與線程關聯起來。“理解這點對正確使用ThreadLocal至關重要。
源碼舉例:
從輸出的結果可以看出,五個線程處理自己的本地變量值。
而這個測試的結果,並沒有相同。這是因為實例為對象,所以初始值為一個對象的引用,那么五個線程的副本就是這個對象的引用的副本,也就是說這些引用還是指向同一個對象,所以就出現了這種情況。
ThreadLocal的接口方法:
- void set(T value):設置當前線程的線程局部變量的值;
1 public void set(T value) { 2 Thread t = Thread.currentThread(); 3 ThreadLocalMap map = getMap(t); 4 5 if (map != null) 6 map.set(this, value); 7 //說明線程第一次使用線程本地變量(注意這里的第一次含義) 8 else 9 createMap(t, value); 10 }
- public T get():該方法返回當前線程所對應的線程局部變量;
1 public T get() { 2 //獲取當前執行線程 3 Thread t = Thread.currentThread(); 4 //取得當前線程的ThreadLocalMap實例 5 ThreadLocalMap map = getMap(t); 6 //如果map不為空,說明該線程已經有了一個ThreadLocalMap實例 7 if (map != null) { 8 //map中保存線程的所有的線程本地變量,我們要去查找當前線程本地變量 9 ThreadLocalMap.Entry e = map.getEntry(this); 10 //如果當前線程本地變量存在這個map中,則返回其對應的值 11 if (e != null) 12 return (T)e.value; 13 } 14 //如果map不存在或者map中不存在當前線程本地變量,返回初始值 15 return setInitialValue(); 16 }
- public void remove():將當前線程局部變量的值刪除,目的是為了減少內存的占用,該方法是JDK 5.0新增的方法。當線程結束后,對應該線程的局部變量將被自動垃圾回收,所以顯示調用該方法清除線程的局部變量並不是必須的操作,但它可以加快內存回收的速度;
- protected T initialValue():返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆蓋而設計的,這個方法是一個延遲調用方法,在線程第1次調用get()或set(T)時才執行,並且僅執行1次。ThreadLocal中的默認實現直接返回一個null。
這里注意,ThreadLocal中是有一個Map,但這個Map不是我們平時使用的Map,而是ThreadLocalMap,ThreadLocalMap是ThreadLocal的一個內部類,不對外使用的。當使用ThreadLocal存值時,首先是獲取到當前線程對象,然后獲取到當前線程本地變量Map,最后將當前使用的ThreadLocal和傳入的值放到Map中,也就是說ThreadLocalMap中存的值是[ThreadLocal對象, 存放的值],這樣做的好處是,每個線程都對應一個本地變量的Map,所以一個線程可以存在多個線程本地變量。下面提供一個簡單的實現版本: