• 【转】ThreadLocal-分析-总结


    ThreadLocal<T>类在Spring,Hibernate等框架中起到了很大的作用,对于其工作原理,很多网上的文章分析的不够彻底,甚至有些误解。

    首先,为了解释ThreadLocal类的工作原理,必须同时介绍与其工作甚密的其他几个类(内部类)

    1.ThreadLocalMap

    2.Thread

    可能有人会觉得Thread与ThreadLocal有什么关系,其实真正的奥秘就在Thread类中的一行:

    Java代码  收藏代码
    1. ThreadLocal.ThreadLocalMap threadLocals = null;  

     其中ThreadLocalMap的定义是在ThreadLocal类中,真正的引用却是在Thread类中

    那么ThreadLocalMap究竟是什么呢?

    可以看到这个类应该是一个Map,JDK的解释是

     写道
    ThreadLocalMap is a customized hash map suitable only for maintaining thread local values

    接下来的重点是ThreadLocalMap中用于存储数据的entry

    Java代码  收藏代码
    1. static class Entry extends WeakReference<ThreadLocal> {  
    2.             /** The value associated with this ThreadLocal. */  
    3.             Object value;  
    4.   
    5.             Entry(ThreadLocal k, Object v) {  
    6.                 super(k);  
    7.                 value = v;  
    8.             }  
    9.         }  

     从中我们可以发现这个Map的key是ThreadLocal变量,value为用户的值,并不是网上大多数的列子key是线程的名字或者标识

    到这里,我们就可以理解ThreadLocal究竟是如何工作的了

    1.Thread类中有一个成员变量叫做ThreadLocalMap,它是一个Map,他的Key是ThreadLocal类

    2.每个线程拥有自己的申明为ThreadLocal类型的变量,所以这个类的名字叫'ThreadLocal':线程自己的(变量)

    3.此变量生命周期是由该线程决定的,开始于第一次初始(get或者set方法)

    4.由ThreadLocal的工作原理决定了:每个线程独自拥有一个变量,并非共享或者拷贝

    Java代码  收藏代码
    1. /** 
    2.  * @author mxdba 
    3.  * 
    4.  */  
    5. public class ThreadLocalSample {  
    6.   
    7.     public static void main(String[] args) {  
    8.         ThreadTest test1 = new ThreadTest(10);  
    9.         ThreadTest test2 = new ThreadTest(20);  
    10.         test1.start();  
    11.         test2.start();  
    12.     }  
    13.   
    14. }  
    15.   
    16. /** 
    17.  * 此线程有两个ThreadLocal变量,但是由于ThreadLocal是延迟初始的, 
    18.  * 所以在debug时可以看到线程名为“线程20”的线程的ThreadLocalMap中没有thLcal2这个entry 
    19.  * @author mxdba 
    20.  *  
    21.  */  
    22. class ThreadTest extends Thread {  
    23.       
    24.     public static ThreadLocal<Integer> thLocal = new ThreadLocal<Integer>();  
    25.     public static ThreadLocal<String> thLocal2 = new ThreadLocal<String>();  
    26.       
    27.     public Integer num;  
    28.       
    29.       
    30.       
    31.     public ThreadTest(Integer num) {  
    32.         super("线程" + num);  
    33.         this.num = num;  
    34.     }  
    35.   
    36.     @Override  
    37.     public void run() {  
    38.         Integer n = thLocal.get();  
    39.         if(num != 20) {  
    40.             String s = thLocal2.get();  
    41.         }  
    42.               
    43.         if(n == null) {  
    44.             thLocal.set(num);  
    45.         }  
    46.         System.out.println(thLocal.get());  
    47.     }  
    48.       
    49. }  

    接下来分析一下源码,就更加清楚了

    关键方法代码  收藏代码
    1. /**  
    2.  * 关键方法,返回当前Thread的ThreadLocalMap  
    3.  * [[[每个Thread返回各自的ThreadLocalMap,所以各个线程中的ThreadLocal均为独立的]]]  
    4.  */  
    5. ThreadLocalMap getMap(Thread t) {  
    6.         return t.threadLocals;  
    7.     }  
    Threadlocal的get方法代码  收藏代码
    1. public T get() {  
    2.         Thread t = Thread.currentThread();  
    3.         /**  
    4.          * 得到当前线程的ThreadLocalMap  
    5.          */  
    6.         ThreadLocalMap map = getMap(t);  
    7.         if (map != null) {  
    8.             /**  
    9.              * 在此线程的ThreadLocalMap中查找key为当前ThreadLocal对象的entry  
    10.              */  
    11.             ThreadLocalMap.Entry e = map.getEntry(this);  
    12.             if (e != null)  
    13.                 return (T)e.value;  
    14.         }  
    15.         return setInitialValue();  
    16.     }  
    初始化方法代码  收藏代码
    1. private T setInitialValue() {  
    2.         /**  
    3.          * 默认返回null,这个方法为protected可以继承  
    4.          */  
    5.         T value = initialValue();  
    6.         Thread t = Thread.currentThread();  
    7.         ThreadLocalMap map = getMap(t);  
    8.         if (map != null)  
    9.             map.set(this, value);  
    10.         else  
    11.             /**  
    12.              * 初次创建  
    13.              */  
    14.             createMap(t, value);  
    15.         return value;  
    16.     }  
    Java代码  收藏代码
    1. /** 
    2.  * 给当前thread初始ThreadlocalMap 
    3.  */  
    4. void createMap(Thread t, T firstValue) {  
    5.         t.threadLocals = new ThreadLocalMap(this, firstValue);  
    6.     }  

    通过上边的分析,我们发现,ThreadLocal类的使用虽然是用来解决多线程的问题的,但是还是有很明显的针对性

    1.最明显的,ThreadLoacl变量的活动范围为某线程,并且我的理解是该线程“专有的,独自霸占”,对该变量的所有操作均有该线程完成!也就是说,ThreadLocal不是用来解决共享,竞争问题的。典型的应用莫过于Spring,Hibernate等框架中对于多线程的处理了

    Java代码  收藏代码
    1. private static final ThreadLocal threadSession = new ThreadLocal();    
    2.     
    3. public static Session getSession() throws InfrastructureException {    
    4.     Session s = (Session) threadSession.get();    
    5.     try {    
    6.         if (s == null) {    
    7.             s = getSessionFactory().openSession();    
    8.             threadSession.set(s);    
    9.         }    
    10.     } catch (HibernateException ex) {    
    11.         throw new InfrastructureException(ex);    
    12.     }    
    13.     return s;    
    14. }    

     这段代码,每个线程有自己的ThreadLocalMap,每个ThreadLocalMap中根据需要初始加载threadSession,这样的好处就是介于singleton与prototype之间,应用singleton无法解决线程,应用prototype开销又太大,有了ThreadLocal之后就好了,对于需要线程“霸占”的变量用ThreadLocal,而该类实例的方法均可以共享。

    2.关于内存泄漏:

    虽然ThreadLocalMap已经使用了weakReference,但是还是建议能够显示的使用remove方法。

    Reference: http://mxdba.iteye.com/blog/777716

    Time is going......
  • 相关阅读:
    js 字符串indexOf方法封装
    js 冒泡排序
    CSS定位 position的三个属性 elative 、absolute、fixed :
    让父元素能感知浮动的子元素 #用伪元素清除浮动
    三个路由器的连接,中间路由的配置(静态路由)
    IDEA 添加tomcat出错: Error: Environment variable name is not set 我的解决方法
    通过基于AspectJ 注解的方式实现Spring AOP报 can't find referenced pointcut myPointCut 错误,我的解决方法
    C语言fopen函数打开文本文件与二进制文件的区别
    位运算的奇技淫巧 系列1
    位运算例子(以后会逐渐补充)
  • 原文地址:https://www.cnblogs.com/shua/p/4403326.html
Copyright © 2020-2023  润新知