多线程(八)~ThreadLocal、InheritableThreadLocal的使用
通过前面的学习,我们了解了在多线程+成员变量等情况下会出现线程安全的问题。那么解决线程安全问题除了使用synchronize关键字之外,还有另一种常用的解决思路,那就是使用ThreadLocal类,下面我们会对这个类做一下简介并指出两者的差异。
ThreadLocal:通过字面翻译过来意思是“线程本地”,代表线程自己的本地变量。在多线程环境下,所有线程会共享类中的成员变量,那么这种情况下有可能会引发线程安全的问题。如果线程有自己的本地变量是不是就不会冲突了,ThreadLocal的原理就是这样,为线程创建自己的本地变量,多个线程间不共享。
线程本地变量最初始是空值null,最最常用的方法就是get()、set(Object value),其实这些值是维护在一个ThreadLocalMap中的。
简单的get()、set()的demo:
package com.multiThread.test.mythreadlocal;
publicclassSampleTest{
//线程本地变量
publicstaticThreadLocal tl =newThreadLocal();
publicstaticvoid main(String[] args){
if(tl.get()==null){
System.out.println("线程初始变量是空值");
tl.set("我的值");
}
System.out.println(tl.get());
System.out.println(tl.get());
}
}
线程变量的隔离性(非共享)
线程本地变量工具类:
package com.multiThread.util;
publicclassThreadLocalUtil{
publicstaticfinalThreadLocal<String> tl =newThreadLocal<String>();
}
线程类:
package com.multiThread.thread;
import com.multiThread.util.ThreadLocalUtil;
publicclassMyThreadLocalAimplementsRunnable{
@Override
publicvoid run(){
for(int i =0;i<100;i++){
ThreadLocalUtil.tl.set("ThreadA:"+(i +1));
System.out.println("ThreadA:"+ThreadLocalUtil.tl.get());
try{
Thread.sleep(20);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
package com.multiThread.thread;
import com.multiThread.util.ThreadLocalUtil;
publicclassMyThreadLocalBimplementsRunnable{
@Override
publicvoid run(){
for(int i =0;i<100;i++){
ThreadLocalUtil.tl.set("ThreadB:"+(i +1));
System.out.println("ThreadB:"+ThreadLocalUtil.tl.get());
try{
Thread.sleep(20);
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
}
测试类:
package com.multiThread.test.mythreadlocal;
import com.multiThread.thread.MyThreadLocalA;
import com.multiThread.thread.MyThreadLocalB;
publicclassMyThreadRun{
publicstaticvoid main(String[] args){
MyThreadLocalA myThreadLocalA =newMyThreadLocalA();
MyThreadLocalB myThreadLocalB =newMyThreadLocalB();
Thread t1 =newThread(myThreadLocalA);
Thread t2 =newThread(myThreadLocalB);
t1.start();
t2.start();
}
}
测试结果:
.
.
.
ThreadB:ThreadB:95
ThreadA:ThreadA:95
ThreadB:ThreadB:96
ThreadA:ThreadA:96
ThreadB:ThreadB:97
ThreadA:ThreadA:97
ThreadA:ThreadA:98
ThreadB:ThreadB:98
ThreadB:ThreadB:99
ThreadA:ThreadA:99
ThreadB:ThreadB:100
ThreadA:ThreadA:100
通过结果可以看出线程A和线程B之间的变量并非共享的,线程本地变量具有隔离性,只是自己用的。
为线程本地变量初始化值:
继承自ThreadLocal类,并实现initialValue(),设置返回值即可。
publicstaticfinalThreadLocal myLocal =newThreadLocal(){
protectedObject initialValue(){
return250;
};
};
System.out.println("默认值为:" + TicketThread.myLocal.get());
默认值为:250
InheritableThreadLocal可以使子线程能从父线程中获取本地变量的值,例如:
子线程类:
package com.wang.threadlocal;
publicclassPersonimplementsRunnable{
@Override
publicvoid run(){
System.out.println("从父线程中获取线程变量:"+MyInheritableThradLocal.threadLocal.get());
}
}
测试类:
package com.wang.threadlocal;
publicclassInheritableTest{
publicstaticvoid main(String[] args){
MyInheritableThradLocal.threadLocal.set("who let the dogs out?");
System.out.println("Main线程中的值为:"+MyInheritableThradLocal.threadLocal.get());
Person person =newPerson();
Thread t1 =newThread(person);
t1.start();
}
}
从例子中可以看出,在main线程中设置的线程本地变量,在子线程中也能拿到。
注意:若子线程获取值的同时,父线程对线程本地变量值做了修改,则子线程取到的值还是原值。