顧名思義,ThreadLocal是為了支援thread-local的變數,它有這樣的特點:
- 對於同一個線程來說,ThreadLocal是全域的,你可以在這個線程的任何地方得到這個ThreadLocal的值。比如在函數A中設定ThreadLocal的值,然後在函數B中得到這個值。
- 對於別的線程來說,ThreadLocal是局部的,你不能這樣做:線上程A中設定ThreadLocal的值,然後線上程B中得到這個值。
ThreadLocal類只有三個成員函數:
- protected Object initialValue() – 返回ThreadLocal變數的初始值。在一線程中,如果在調用set()之前調用get()的時候,initialValue()就會被調用,而且也只會被調用一次。如果在調用get()之前已經調用過set()了,那麼initialValue()就不會被調用。
- public Object get() – 得到這個ThreadLocal變數的值(對於當前線程的)。
- public void set(Object value) – 設定這個ThreadLocal的變數的值(對於當前線程的)。
用處:
當你需要把一個單線程程式移植到多線程的環境的時候,你可以根據實際情況把一些全域變數用ThreadLocal封裝起來,從而實現安全執行緒。但是不要濫用ThreadLocal:你在函數A設定一個ThreadLocal,然後函數B中使用這個ThreadLocal,也就意味著,B依賴於A。
需要注意的事項:
從JDK的原始碼來看,在get的時候,是根據ThreadLocal的reference來找實際的值,那麼在set()和get()之間不要去改變ThreadLocal的reference,比如new一個新的ThreadLocal。
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value ; } return setInitialValue();}
一個錯誤的例子:在多線程環境下,如果調用順序是線程A調用setValue(oa),線程B調用setValue(ob),線程A調用getValue(),那麼將得到一個空值。因為tl的reference已經改變了。
public class ThreadLocalTest { static ThreadLocal tl = null; public static void setValue(Object o) { tl = new ThreadLocal(); tl.set(o); } public static Object getValue() { return tl.get(); }}