InheritableThreadLocal類繼承於ThreadLocal類,因此它具備ThreadLocal類的特性,但又是一種特殊的ThreadLocal,其特殊性在於InheritableThreadLocal變量值會自動傳遞給全部子線程,而普通ThreadLocal變量不行。那麼子線程是否能夠修改InheritableThreadLocal變量值而後反向傳遞給主線程了?咱們先來看一組測試代碼和結果:html
import java.util.concurrent.TimeUnit; public class TestThreadLocal { private static ThreadLocal<String> stringItl = new InheritableThreadLocal<String>(){ protected String initialValue() { System.out.println(Thread.currentThread().getName() + " initialize stringItl variable."); return "String init"; } }; private static ThreadLocal<String> stringItl2 = new InheritableThreadLocal<String>(){ protected String initialValue() { System.out.println(Thread.currentThread().getName() + " initialize stringItl2 variable."); return "String2 init"; } }; private static ThreadLocal<StringBuffer> stringBufferItl = new InheritableThreadLocal<StringBuffer>(){ protected StringBuffer initialValue() { System.out.println(Thread.currentThread().getName() + " initialize stringBufferItl variable."); return new StringBuffer("StringBuffer init"); } }; private static ThreadLocal<StringBuffer> stringBufferItl2 = new InheritableThreadLocal<StringBuffer>(){ protected StringBuffer initialValue() { System.out.println(Thread.currentThread().getName() + " initialize stringBufferItl2 variable."); return new StringBuffer("StringBuffer2 init"); } }; public static void main(String[] args) throws InterruptedException { stringItl.set("Parent"); stringBufferItl.set(new StringBuffer("ParentBuffer")); System.out.println(Thread.currentThread().getName() + " first get stringItl : " + stringItl.get()); System.out.println(Thread.currentThread().getName() + " first get stringBufferItl : " + stringBufferItl.get().toString()); for(int i=0; i<2; i++){ new Thread(){ public void run(){ System.out.println(Thread.currentThread().getName() + " first get stringItl : " + stringItl.get()); stringItl.set(Thread.currentThread().getName() + "Child"); System.out.println(Thread.currentThread().getName() + " get after set stringItl : " + stringItl.get()); System.out.println(Thread.currentThread().getName() + " first get stringItl2 : " + stringItl2.get()); stringItl2.set(Thread.currentThread().getName() + "Child"); System.out.println(Thread.currentThread().getName() + " get after set stringItl2 : " + stringItl2.get()); System.out.println(Thread.currentThread().getName() + " first get stringBufferItl : " + stringBufferItl.get().toString()); stringBufferItl.get().append(Thread.currentThread().getName()); System.out.println(Thread.currentThread().getName() + " get after set stringBufferItl : " + stringBufferItl.get().toString()); System.out.println(Thread.currentThread().getName() + " first get stringBufferIt2 : " + stringBufferItl2.get().toString()); stringBufferItl2.get().append(Thread.currentThread().getName()); System.out.println(Thread.currentThread().getName() + " get after set stringBufferItl2 : " + stringBufferItl2.get().toString()); } }.start(); } for(int i=0; i<2; i++){ new Thread(){ public void run(){ System.out.println(Thread.currentThread().getName() + " first get stringItl : " + stringItl.get()); stringItl.set(Thread.currentThread().getName() + "Child"); System.out.println(Thread.currentThread().getName() + " get after set stringItl : " + stringItl.get()); System.out.println(Thread.currentThread().getName() + " first get stringItl2 : " + stringItl2.get()); stringItl2.set(Thread.currentThread().getName() + "Child"); System.out.println(Thread.currentThread().getName() + " get after set stringItl2 : " + stringItl2.get()); System.out.println(Thread.currentThread().getName() + " first get stringBufferItl : " + stringBufferItl.get().toString()); stringBufferItl.set(new StringBuffer(Thread.currentThread().getName() + "Buffer")); System.out.println(Thread.currentThread().getName() + " get after set stringBufferItl : " + stringBufferItl.get().toString()); System.out.println(Thread.currentThread().getName() + " first get stringBufferIt2 : " + stringBufferItl2.get().toString()); stringBufferItl2.get().append(Thread.currentThread().getName()); System.out.println(Thread.currentThread().getName() + " get after set stringBufferItl2 : " + stringBufferItl2.get().toString()); } }.start(); } TimeUnit.SECONDS.sleep(2);//let children threads run first System.out.println(Thread.currentThread().getName() + " second get stringItl : " + stringItl.get()); System.out.println(Thread.currentThread().getName() + " first get stringItl2 : " + stringItl2.get()); System.out.println(Thread.currentThread().getName() + " second get stringBufferItl : " + stringBufferItl.get().toString()); System.out.println(Thread.currentThread().getName() + " first get stringBufferItl2 : " + stringBufferItl2.get().toString()); } }
代碼運行結果:java
main first get stringItl : Parent main first get stringBufferItl : ParentBuffer Thread-0 first get stringItl : Parent Thread-0 get after set stringItl : Thread-0Child Thread-0 initialize stringItl2 variable. Thread-0 first get stringItl2 : String2 init Thread-0 get after set stringItl2 : Thread-0Child Thread-0 first get stringBufferItl : ParentBuffer Thread-0 get after set stringBufferItl : ParentBufferThread-0 Thread-0 initialize stringBufferItl2 variable. Thread-0 first get stringBufferIt2 : StringBuffer2 init Thread-0 get after set stringBufferItl2 : StringBuffer2 initThread-0 Thread-1 first get stringItl : Parent Thread-1 get after set stringItl : Thread-1Child Thread-1 initialize stringItl2 variable. Thread-1 first get stringItl2 : String2 init Thread-1 get after set stringItl2 : Thread-1Child Thread-1 first get stringBufferItl : ParentBufferThread-0 Thread-1 get after set stringBufferItl : ParentBufferThread-0Thread-1 Thread-1 initialize stringBufferItl2 variable. Thread-1 first get stringBufferIt2 : StringBuffer2 init Thread-1 get after set stringBufferItl2 : StringBuffer2 initThread-1 Thread-3 first get stringItl : Parent Thread-3 get after set stringItl : Thread-3Child Thread-3 initialize stringItl2 variable. Thread-3 first get stringItl2 : String2 init Thread-3 get after set stringItl2 : Thread-3Child Thread-3 first get stringBufferItl : ParentBufferThread-0Thread-1 Thread-3 get after set stringBufferItl : Thread-3Buffer Thread-3 initialize stringBufferItl2 variable. Thread-3 first get stringBufferIt2 : StringBuffer2 init Thread-3 get after set stringBufferItl2 : StringBuffer2 initThread-3 Thread-2 first get stringItl : Parent Thread-2 get after set stringItl : Thread-2Child Thread-2 initialize stringItl2 variable. Thread-2 first get stringItl2 : String2 init Thread-2 get after set stringItl2 : Thread-2Child Thread-2 first get stringBufferItl : ParentBufferThread-0Thread-1 Thread-2 get after set stringBufferItl : Thread-2Buffer Thread-2 initialize stringBufferItl2 variable. Thread-2 first get stringBufferIt2 : StringBuffer2 init Thread-2 get after set stringBufferItl2 : StringBuffer2 initThread-2 main second get stringItl : Parent main initialize stringItl2 variable. main first get stringItl2 : String2 init main second get stringBufferItl : ParentBufferThread-0Thread-1 main initialize stringBufferItl2 variable. main first get stringBufferItl2 : StringBuffer2 init
從運行結果能夠看出:安全
若是ThreadLocal存儲的是不變性(immutable)的對象,如String,對於主線程設置的值子線程能夠經過get函數獲取,但子線程調用set函數設置新值後,對主線程沒有影響,對其它子線程也沒有影響,只對本身可見,(請參考代碼例子中的stringItl變量);若是主線程尚未獲取(get)或者設置(set)過ThreadLocal變量,而子線程先獲取(get)或者設置(set)了ThreadLocal變量,那麼這個份值只屬於那個子線程,對主線程和其它子線程都不可見,(請參考代碼例子中的stringIt2變量).app
若是ThreadLocal存儲的是可變性(mutable)的對象,如StringBuffer,對於主線程設置的值子線程能夠經過get函數獲取,但子線程調用set函數設置新值後,對主線程沒有影響,對其它子線程也沒有影響,只對本身可見,但若是子線程先get獲取再修改對象的屬性,那麼這個修改對主線程和其它子線程是可見的,即他們仍是共享一個引用(請參考代碼例子中的stringBufferItl變量);若是主線程尚未獲取(get)或者設置(set)過ThreadLocal變量,而子線程先獲取(get)或者設置(set)了ThreadLocal變量,那麼這份值只屬於那個子線程,對主線程和其它子線程都不可見,(請參考代碼例子中的stringBufferItl2變量).ide
因此子線程只能經過修改可變性(Mutable)對象對主線程纔是可見的,即才能將修改傳遞給主線程,但這不是一種好的實踐,不建議使用,爲了保護線程的安全性,通常建議只傳遞不可變(Immuable)對象,即沒有狀態的對象。函數
關於ThreadLocal,你們能夠參考如下兩篇文章:測試
http://www.ibm.com/developerworks/cn/java/j-threads/index3.html線程
http://geekexplains.blogspot.sg/2009/02/threadlocal-inheritablethreadlocal-in.htmlhtm