ThreadLocal:
為解決多線程程式的并發問題提供了一種新的思路。使用這個工具類可以很簡潔地編寫出優美的多線程程式。
當使用ThreadLocal維護變量時,ThreadLocal為每個使用該變量的線程提供獨立的變量副本,是以每一個線程都可以獨立地改變自己的副本,而不會影響其它線程所對應的副本。
從線程的角度看,目标變量就象是線程的本地變量,這也是類名中“Local”所要表達的意思。
ThreadLocal的接口方法
ThreadLocal類接口很簡單,隻有4個方法,我們先來了解一下:
void set(Object value)設定目前線程的線程局部變量的值。
public Object get()該方法傳回目前線程所對應的線程局部變量。
public void remove()将目前線程局部變量的值删除,目的是為了減少記憶體的占用,該方法是JDK 5.0新增的方法。需要指出的是,當線程結束後,對應該線程的局部變量将自動被垃圾回收,是以顯式調用該方法清除線程的局部變量并不是必須的操作,但它可以加快記憶體回收的速度。
protected Object initialValue()傳回該線程局部變量的初始值,該方法是一個protected的方法,顯然是為了讓子類覆寫而設計的。這個方法是一個延遲調用方法,線上程第1次調用get()或set(Object)時才執行,并且僅執行1次。ThreadLocal中的預設實作直接傳回一個null。
值得一提的是,在JDK5.0中,ThreadLocal已經支援泛型,
該類的類名已經變為ThreadLocal<T>。API方法也相應進行了調整,新版本的API方法分别是void set(T value)、T get()以及T initialValue()。
ThreadLocal是如何做到為每一個線程維護變量的副本的呢?
其實實作的思路很簡單:在ThreadLocal類中有一個Map,用于存儲每一個線程的變量副本,Map中元素的鍵為線程對象,而值對應線程的變量副本。
ThreadLocal源碼源碼的人都知道,ThreadLocal的設計的确巧妙,但是它也有一個缺陷:
可能會引起記憶體洩漏;
ThreadLocalMap中key維護着一個weakReference,它在下次GC之前會被清理,如果Value仍然保持着外部的強引用,該ThreadLocal沒有再進行set,get或者remove操作,時間長了就可能導緻OutOfMemoryError .
lucene中的類CloseableThreadLocal對ThreadLocal做了處理,優化了其缺陷.學習一下:
當執行CloseableThreadLocal.set(T)時,内部其實隻是把值賦給内部的ThreadLocal對象,即執行ThreadLocal.set(new WeakReference(T)),将T包裝成弱引用對象,目的就是當記憶體不足時,jvm可以回收此對象.
這樣引入一個新的問題:目前線程還存活着的時候,因為記憶體不足而回收了弱引用對象,這樣會在下次調用get()時取不到值傳回null,這是不可接受的.是以CloseableThreadLocal在内部還建立了一個私有的Map,WeakHashMap<Thread, T>,當線程隻要存活時,則T就至少有一個引用存在,是以不會被提前回收。同時要注意另一個問題,要對WeakHashMap的操作使用synchronized做同步.
線程池中的線程使用threadlocal會出現什麼問題?
線程池中的線程在任務執行完成後會被複用,是以線上程執行完成時,要對 ThreadLocal 進行清理(清除掉與本線程相關聯的 value 對象)。不然,被複用的線程去執行新的任務時會使用被上一個線程操作過的 value 對象,進而産生不符合預期的結果。