InheritableThreadLocal变量的可见性

转载自:http://stevex.blog.51cto.com/4300375/1289499


InheritableThreadLocal类继承于ThreadLocal类,所以它具有ThreadLocal类的特性,但又是一种特殊的ThreadLocal,其特殊性在于InheritableThreadLocal变量值会自动传递给所有子线程,而普通ThreadLocal变量不行。那么子线程是否可以修改InheritableThreadLocal变量值然后反向传递给主线程了?我们先来看一组测试代码和结果:

import java.util.concurrent.TimeUnit;

public class TestMain {

	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 { 
    	
    	System.out.println(Thread.currentThread().getName() + " first get stringItl : " + stringItl.get()); 
        System.out.println(Thread.currentThread().getName() + " first get stringBufferItl : " + stringBufferItl.get().toString()); 
        
        stringItl.set("Parent"); 
        stringBufferItl.set(new StringBuffer("ParentBuffer")); 
                                                                                                                                                                                                      
        System.out.println(Thread.currentThread().getName() + " get after set stringItl : " + stringItl.get()); 
        System.out.println(Thread.currentThread().getName() + " get after set 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()); 
    } 
}

代码运行结果:

main initialize stringItl variable.
main first get stringItl : String init
main initialize stringBufferItl variable.
main first get stringBufferItl : StringBuffer init
main get after set stringItl : Parent
main get after set stringBufferItl : ParentBuffer
//此时 stringItl->"Parent" stringBufferItl->"ParentBuffer"


Thread-0 first get stringItl : Parent			//Thread-0不用初始化stringItl
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-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

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

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

从运行结果可以看出:

如果InheritableThreadLocal存储的是不变性(immutable)的对象,如String,对于主线程设置的值子线程可以通过get函数获取,但子线程调用set函数设置新值后,对主线程没有影响,对其它子线程也没有影响,只对自己可见,(请参考代码例子中的stringItl变量);如果主线程还没有获取(get)或者设置(set)过ThreadLocal变量,而子线程先获取(get)或者设置(set)了ThreadLocal变量,那么这个份值只属于那个子线程,对主线程和其它子线程都不可见,(请参考代码例子中的stringIt2变量).


如果InheritableThreadLocal存储的是可变性(mutable)的对象,如StringBuffer,对于主线程设置的值子线程可以通过get函数获取,但子线程调用set函数设置新值后,对主线程没有影响,对其它子线程也没有影响,只对自己可见,但如果子线程先get获取再修改对象的属性,那么这个修改对主线程和其它子线程是可见的,即他们还是共享一个引用(请参考代码例子中的stringBufferItl变量);如果主线程还没有获取(get)或者设置(set)过ThreadLocal变量,而子线程先获取(get)或者设置(set)了ThreadLocal变量,那么这份值只属于那个子线程,对主线程和其它子线程都不可见,(请参考代码例子中的stringBufferItl2变量).


所以子线程只能通过修改可变性(Mutable)对象对主线程才是可见的,即才能将修改传递给主线程,但这不是一种好的实践,不建议使用,为了保护线程的安全性,一般建议只传递不可变(Immuable)对象,即没有状态的对象。


关于ThreadLocal,大家可以参考以下两篇文章:

http://www.ibm.com/developerworks/cn/java/j-threads/index3.html

http://geekexplains.blogspot.sg/2009/02/threadlocal-inheritablethreadlocal-in.html



本文出自 “力量来源于赤诚的爱!” 博客,请务必保留此出处http://stevex.blog.51cto.com/4300375/1289499




个人理解:

对于InheritableThreadLocal来说,分2种情况:

1、存储Immuable对象,此时等价于ThreadLocal+可见性(如果对象被main thread设置了),即对修改互不相干,但是子线程能获取到父线程已设置的值。

2、存储Mutable对象时,与Immuable的区别是:对于StringBuffer,其实存储的是引用,所以子线程append后,主线程也能看到。但是不能被set,set会产生新的引用,就会各自独立了。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值