InheritableThreadLocal变量的可见性

本文探讨了InheritableThreadLocal类的特性及其与ThreadLocal的区别。通过代码示例展示了如何在子线程中继承父线程的变量,并讨论了不可变对象与可变对象在父子线程间传递的影响。

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


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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());
     }
}

代码运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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  var iable.
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  var iable.
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  var iable.
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  var iable.
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  var iable.
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  var iable.
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  var iable.
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  var iable.
Thread- 2  first  get  stringBufferIt2 : StringBuffer2 init
Thread- 2  get  after  set  stringBufferItl2 : StringBuffer2 initThread- 2
main second  get  stringItl : Parent
main initialize stringItl2  var iable.
main first  get  stringItl2 : String2 init
main second  get  stringBufferItl : ParentBufferThread-0Thread- 1
main initialize stringBufferItl2  var iable.
main first  get  stringBufferItl2 : StringBuffer2 init


从运行结果可以看出:

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


如果ThreadLocal存储的是可变性(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




     本文转自sarchitect 51CTO博客,原文链接:,http://blog.51cto.com/stevex/1289499如需转载请自行联系原作者





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值