多线程(八)~ThreadLocal、InheritableThreadLocal的使用

    通过前面的学习,我们了解了在多线程+成员变量等情况下会出现线程安全的问题。那么解决线程安全问题除了使用synchronize关键字之外,还有另一种常用的解决思路,那就是使用ThreadLocal类,下面我们会对这个类做一下简介并指出两者的差异。

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





posted @ 2017-02-27 14:14  鱼我所欲也  阅读(1126)  评论(0编辑  收藏  举报