InheritableThreadLocal类继承于ThreadLocal类,所以它具有ThreadLocal类的特性,但又是一种特殊的ThreadLocal,其特殊性在于InheritableThreadLocal变量值会自动传递给所有子线程,而普通ThreadLocal变量不行。那么子线程是否可以修改InheritableThreadLocal变量值然后反向传递给主线程了?我们先来看一组测试代码和结果:
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());
}
}
代码运行结果:
main first get stringItl : Parentmain first get stringBufferItl : ParentBufferThread-0 first get stringItl : ParentThread-0 get after set stringItl : Thread-0ChildThread-0 initialize stringItl2 variable.Thread-0 first get stringItl2 : String2 initThread-0 get after set stringItl2 : Thread-0ChildThread-0 first get stringBufferItl : ParentBufferThread-0 get after set stringBufferItl : ParentBufferThread-0Thread-0 initialize stringBufferItl2 variable.Thread-0 first get stringBufferIt2 : StringBuffer2 initThread-0 get after set stringBufferItl2 : StringBuffer2 initThread-0Thread-1 first get stringItl : ParentThread-1 get after set stringItl : Thread-1ChildThread-1 initialize stringItl2 variable.Thread-1 first get stringItl2 : String2 initThread-1 get after set stringItl2 : Thread-1ChildThread-1 first get stringBufferItl : ParentBufferThread-0Thread-1 get after set stringBufferItl : ParentBufferThread-0Thread-1Thread-1 initialize stringBufferItl2 variable.Thread-1 first get stringBufferIt2 : StringBuffer2 initThread-1 get after set stringBufferItl2 : StringBuffer2 initThread-1Thread-3 first get stringItl : ParentThread-3 get after set stringItl : Thread-3ChildThread-3 initialize stringItl2 variable.Thread-3 first get stringItl2 : String2 initThread-3 get after set stringItl2 : Thread-3ChildThread-3 first get stringBufferItl : ParentBufferThread-0Thread-1Thread-3 get after set stringBufferItl : Thread-3BufferThread-3 initialize stringBufferItl2 variable.Thread-3 first get stringBufferIt2 : StringBuffer2 initThread-3 get after set stringBufferItl2 : StringBuffer2 initThread-3Thread-2 first get stringItl : ParentThread-2 get after set stringItl : Thread-2ChildThread-2 initialize stringItl2 variable.Thread-2 first get stringItl2 : String2 initThread-2 get after set stringItl2 : Thread-2ChildThread-2 first get stringBufferItl : ParentBufferThread-0Thread-1Thread-2 get after set stringBufferItl : Thread-2BufferThread-2 initialize stringBufferItl2 variable.Thread-2 first get stringBufferIt2 : StringBuffer2 initThread-2 get after set stringBufferItl2 : StringBuffer2 initThread-2main second get stringItl : Parentmain initialize stringItl2 variable.main first get stringItl2 : String2 initmain second get stringBufferItl : ParentBufferThread-0Thread-1main initialize stringBufferItl2 variable.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
本文出自 “力量来源于赤诚的爱!” 博客,请务必保留此出处http://stevex.blog.51cto.com/4300375/1289499