InheritableThreadLocal變量的可見性

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

相關文章
相關標籤/搜索