多线程(八)~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{@Overridepublicvoid 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{@Overridepublicvoid 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:95ThreadA:ThreadA:95ThreadB:ThreadB:96ThreadA:ThreadA:96ThreadB:ThreadB:97ThreadA:ThreadA:97ThreadA:ThreadA:98ThreadB:ThreadB:98ThreadB:ThreadB:99ThreadA:ThreadA:99ThreadB:ThreadB:100ThreadA: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{@Overridepublicvoid 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线程中设置的线程本地变量,在子线程中也能拿到。
注意:若子线程获取值的同时,父线程对线程本地变量值做了修改,则子线程取到的值还是原值。
浙公网安备 33010602011771号