JDK源码解析之 java.lang.ThreadLocal
此類提供線程局部變量。這些變量與普通變量不同,每個訪問一個線程(通過其get或set方法)的線程 都有其自己的,獨立初始化的變量副本。 ThreadLocal實例通常是希望將狀態與線程關聯的類中的私有靜態字段(例如,用戶ID或事務ID)。
以射擊游戲舉例,游戲開始時,每個人能夠領到一把槍,槍把上有三個數字:子彈數、殺敵數、自己的命數,為其設置的初始值分別為100、0、10.設戰場上的每個人都是一個線程,那么這三個初始值寫在哪里呢?
如果每個線程都寫死這三個值,萬一將初始子彈數統一改成 1000發呢?
如果共享,那么線程之間的并發修改會導致數據不準確.
能不能構造這樣一個對象,將這個對象設置為共享變量,統一設置初始值,但是每個線程對這個值的修改都是互相獨立的.這個對象就是ThreadLocal
一、類定義
public class ThreadLocal<T> {}…用來限制Class中的參數類型,確保Class中參數的一致性
二、實例變量和相關方法
//用于ThreadLocalMap private final int threadLocalHashCode = nextHashCode();//下一個hash code,從0開始 private static AtomicInteger nextHashCode = new AtomicInteger();//hash增量 private static final int HASH_INCREMENT = 0x61c88647;//在獲取下一個hash code private static int nextHashCode() {return nextHashCode.getAndAdd(HASH_INCREMENT); }三、內部類
內部類:ThreadLocalMap
ThreadLocalMap負責存儲ThreadLocal及其變量,即ThreadLocal對象本身作為鍵,ThreadLocal存儲的變量作為值。每個Thread對象在聲明一個ThreadLocal后會持有一個ThreadLocalMap的引用,來實現ThreadLocal的功能。
ThreadLocalMap持有一個內部類Entry,類似于HashMap.Node類,負責保存鍵值對。
static class Entry extends WeakReference<ThreadLocal<?>> {Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;} }Entry繼承了WeakReference類,使Entry的鍵為弱引用。
看到這里很多人或許有這樣一個疑問:為什么Entry要繼承WeakReference?
既然將ThreadLocal聲明為弱引用,那么自然會聯想到和GC有關。
如果不聲明為弱引用,以最上面Test類的代碼為例,當我們將上述ThreadLocal類型的靜態變量tl設置為null時,Thread對象成員變量threadLocals依然保留有一個ThreadLocalMap,該Map中持有保存該ThreadLocal的Entry,在這個線程運行期間無法GC,從而引發內存泄漏。所以,Entry的鍵要聲明為弱引用。
四、主要方法
1.get()
返回此線程局部變量的當前線程副本中的值。
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue(); }首先是獲取當前運行線程對象,然后根據該線程對象找到對應的ThreadLocalMap。
- 如果找到了該線程對應的ThreadLocalMap,則通過當前ThreadLocal對象作為鍵查找Map中對應的Entry(鍵值對)對象
- 如果查找結果不為null,則返回Entry對象的value。否則調用setInitialValue方法將當前ThreadLocal對象(this)和變量作為鍵值對存入ThreadLocalMap并返回變量。
2.initialValue()
為變量設置初始值,該方法的默認實現是:
protected T initialValue() {return null; }如果想要為該變量設置一個初始值,只需重寫該方法即可,例如:
@Override protected String initialValue() {return "hello world"; }3.set(T value)
與get方法類似,set方法首先會獲取當前運行的Thread對象并通過該對象獲取對應的ThreadLocalMap,如果map為空,則為當前Thread對象新建一個ThreadLocalMap,否則直接將value放入map中。
public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value); }4、remove()
同樣,獲取當前Thread對應的ThreadLocalMap,然后調用ThreadLocalMap的remove方法移除ThreadLocal對象,無需通過弱引用機制對該ThreadLocal對象進行GC。
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());if (m != null)m.remove(this); }五、總結
ThreadLocal是如何做到為每一個線程維護變量的副本的呢?
在ThreadLocal類中設置了一個Map,存儲每一個線程的變量的副本。
ThreadLocal使用場合主要解決多線程中數據數據因并發產生不一致問題。ThreadLocal為每個線程的中并發訪問的數據提供一個副本,通過訪問副本來運行業務,這樣的結果是耗費了內存,單大大減少了線程同步所帶來性能消耗,也減少了線程并發控制的復雜度。
Synchronized用于線程間的數據共享,而ThreadLocal則用于線程間的數據隔離。
Threadlocal底層是通過threadlocalMap進行存儲鍵值 每個ThreadLocal類創建一個Map,然后用線程的ID作為Map的key,實例對象作為Map的value,這樣就能達到各個線程的值隔離的效果。
ThreadLocal的作用是提供線程內的局部變量,這種變量在線程的生命周期內起作用,減少同一個線程內多個函數或者組件之間一些公共變量的傳遞的復雜度。
總結
以上是生活随笔為你收集整理的JDK源码解析之 java.lang.ThreadLocal的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 认识VLAN,并学会VLAN的划分和网络
- 下一篇: jar包在Hadoop集群上测试(Map