首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

ThreadLocal-分析-小结

2012-09-24 
ThreadLocal-分析-总结转载: 讨论帖 http://www.iteye.com/topic/777716

ThreadLocal-分析-总结

==============转载: 讨论帖 http://www.iteye.com/topic/777716==================

?

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

?

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

1.ThreadLocalMap

2.Thread

ThreadLocal-分析-小结

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

?

?ThreadLocal-分析-小结ThreadLocal-分析-小结
    ThreadLocal.ThreadLocalMap threadLocals = null;

    ?

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

    ?

    那么ThreadLocalMap究竟是什么呢?

    ?

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

    ?

    ?ThreadLocal-分析-小结ThreadLocal-分析-小结
      static class Entry extends WeakReference<ThreadLocal> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k); value = v; } }

      ?

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

      ?

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

      ?

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

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

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

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

      ?

      ?ThreadLocal-分析-小结ThreadLocal-分析-小结
        /** * @author mxdba * */public class ThreadLocalSample {public static void main(String[] args) {ThreadTest test1 = new ThreadTest(10);ThreadTest test2 = new ThreadTest(20);test1.start();test2.start();}}/** * 此线程有两个ThreadLocal变量,但是由于ThreadLocal是延迟初始的, * 所以在debug时可以看到线程名为“线程20”的线程的ThreadLocalMap中没有thLcal2这个entry * @author mxdba * */class ThreadTest extends Thread {public static ThreadLocal<Integer> thLocal = new ThreadLocal<Integer>();public static ThreadLocal<String> thLocal2 = new ThreadLocal<String>();public Integer num;public ThreadTest(Integer num) {super("线程" + num);this.num = num;}@Overridepublic void run() {Integer n = thLocal.get();if(num != 20) {String s = thLocal2.get();}if(n == null) {thLocal.set(num);}System.out.println(thLocal.get());}}

        ?

        ?

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

        ?

        ?ThreadLocal-分析-小结ThreadLocal-分析-小结
          /** * 关键方法,返回当前Thread的ThreadLocalMap * [[[每个Thread返回各自的ThreadLocalMap,所以各个线程中的ThreadLocal均为独立的]]] */ThreadLocalMap getMap(Thread t) { return t.threadLocals; }?

          ?

          ?

          ?

          ?ThreadLocal-分析-小结ThreadLocal-分析-小结
            public T get() { Thread t = Thread.currentThread(); /** * 得到当前线程的ThreadLocalMap */ ThreadLocalMap map = getMap(t); if (map != null) { /** * 在此线程的ThreadLocalMap中查找key为当前ThreadLocal对象的entry */ ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }?

            ?

            ?

            ?

            ?ThreadLocal-分析-小结ThreadLocal-分析-小结
              private T setInitialValue() { /** * 默认返回null,这个方法为protected可以继承 */ T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else /** * 初次创建 */ createMap(t, value); return value; }?

              ?

              ?

              ?ThreadLocal-分析-小结ThreadLocal-分析-小结
                /** * 给当前thread初始ThreadlocalMap */void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }?

                ?

                ?

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

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

                下面来看一个hibernate中典型的ThreadLocal的应用:

                ?

                ?ThreadLocal-分析-小结ThreadLocal-分析-小结
                  private static final ThreadLocal threadSession = new ThreadLocal(); public static Session getSession() throws InfrastructureException { Session s = (Session) threadSession.get(); try { if (s == null) { s = getSessionFactory().openSession(); threadSession.set(s); } } catch (HibernateException ex) { throw new InfrastructureException(ex); } return s; }

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

                  ?

                  2.关于内存泄漏:

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

                  ?

                  ?

                  ==============转载: 讨论帖 http://ari.iteye.com/blog/757478==================

                  ?

                  一、ThreadLocal概述

                  ?????? 学习JDK中的类,首先看下JDK API对此类的描述,描述如下:

                  ?ThreadLocal-分析-小结ThreadLocal-分析-小结
                    public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) return (T)e.value; } return setInitialValue(); }

                    ????调用get方法如果此Map不存在首先初始化,创建此map,将线程为key,初始化的vlaue存入其中,注意此处的initialValue,我们可以覆盖此方法,在首次调用时初始化一个适当的值。setInitialValue代码如下:

                    ?ThreadLocal-分析-小结ThreadLocal-分析-小结
                      private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }

                      ?

                      ??? set方法相对比较简单如果理解以上俩个方法,获取当前线程的引用,从map中获取该线程对应的map,如果map存在更新缓存值,否则创建并存储,代码如下:

                      ?ThreadLocal-分析-小结ThreadLocal-分析-小结
                        public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }

                        ?

                        ??? 对于ThreadLocal在何处存储变量副本,我们看getMap方法:获取的是当前线程的ThreadLocal类型的threadLocals属性。显然变量副本存储在每一个线程中。

                        ?

                        ?ThreadLocal-分析-小结ThreadLocal-分析-小结
                          /** * 获取线程的ThreadLocalMap 属性实例 */ThreadLocalMap getMap(Thread t) { return t.threadLocals; }

                          ?

                          ??? 上面我们知道变量副本存放于何处,这里我们简单说下如何被java的垃圾收集机制收集,当我们不在使用是调用set(null),此时不在将引用指向该‘map’,而线程退出时会执行资源回收操作,将申请的资源进行回收,其实就是将属性的引用设置为null。这时已经不在有任何引用指向该map,故而会被垃圾收集。

                          ?

                          ?四、ThreadLocal应用示例

                          ?

                          ????? 在我的另一篇文章,对ThreadLocal的使用做了一个实例,此示例也可以用作生产环境,请参见:http://ari.iteye.com/blog/757641

                          ?

                          ?

热点排行