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如需转载请自行联系原作者