• Java并发机制(4)--ThreadLocal线程本地变量(转)


    个人理解:

      说明:看了博客园中大神写的ThreadLocal的详解,感觉还是有些迷糊,下面用自己的理解简单描述下ThreadLocal的机制(难免有误);

      1、首先ThreadLocal用于存储对应线程的本地变量,放在哪里呢?每一个线程维护一个threadlocals(这个threadlocals我认为是由ThreadLcoal创建的,是当前线程上的属性,多个ThreadLocal只创建一个:通过ThreadLocal中的createMap方法: t.threadLocals = new ThreadLocalMap(this, firstValue); ),它事实上就是一个ThreadLocalMap,是ThreadLocal的内部类,可以理解为一张Map表,其中包含了Entry键值对,故线程的本地变量就放在这个Entry中:Entry:<ThreadLocal,value>形式,键值对里的ThreadLocal我认为是你用的时候new出来的ThreadLocal,而不是每个线程所维护的那一个ThreadLoca。(不是很确定,但是想到一个线程要放多个变量的话,每个变量new一个ThreadLocal,这些ThreadLocal做为key放在线程所维护的那个ThreadLocalMap中?)。

      2、由1中知道,一个ThreadLocal只能存放一个线程的本地变量,所以要存放第二个线程的本地变量,就应该在new ThreadLocal进行存储,所以  private static final ThreadLocal <T> t1=new Threadlocal<>()  这句话可以写在工具类中,且作为静态成员变量使用。

      这里我一开始理解错误:把ThreadLocalMap理解成类似于HashMaple了,然后ThreadLocal保存了所有线程的本地变量,这样一个ThreadLocal就可以了?那么多线程访问一个ThreadLocal(共享)依然需要加锁,失去了原来的意义。

      3、ThreadLocal的方法理解(源码分析):

        3.1、threadlocal.get()方法:

            step1:根据当前线程t获取对应的TheradLocalMap;

            step2:如果ThreadLocalMap!=null;获取内部的Entry,返回val,结束;

                 如果ThreadLocalMap==null,调用setInitialValue()方法设置初始值;

            step3:setInitialValue()方法判断是否有ThreadLocalMap,(没有则创建),

                并调用initialValue()方法获得初始value,这个value=null;

            所以,new 出来的Threadlocal要先set(value),然后get(),否则get到null;

              或者也可以重写initialValue方法(默认返回一个初始值);

        3.2、threadlocal.set()方法:赋值,不多描述。

      4、弱引用问题:ThreadLocalMap使用ThreadLocal的弱引用作为key,,如果一个ThreadLocal没有外部强引用引用他,那么系统gc的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,无法访问到这些value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value,永远无法回收,造成内存泄露。

       JDK建议将ThreadLocal变量定义成private static的,这样的话ThreadLocal的生命周期就更长,并主动调用remove函数释放。

      总结如下:

        首先,要区分threadlocals、ThreadLocalMap、ThreadLocal这三者。

        其次,ThreadLcoal的主要机制如下。

        (1)每个线程Thread内部有一个threadlocals属性,它是ThreadLocal.ThreadLocalMap类型的,内部的Entry<ThreadLocal,value>用来保存变量的副本,只供线程自己使用。

        (2)最开始,threadlocals是空的,通过Threadlocal的get或者set方法可以初始化threadlocals(调用createMap方法,源代码在1种),然后你每存一个变量副本,需要new ThreadLocal()出来,存几个new几个,threadlocals以该ThreadLocal为key,变量副本为value进行保存。

        (2)我们可以通过ThreadLocal.get()获取对应的ThreadLocal的value。通过ThreadLcoal.set(value)来设置更改threadlocals中的值。

    参考资料:

      1、博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920407.html

      2、http://www.iteye.com/topic/103804

      3、http://www.cnblogs.com/xzwblog/p/7227509.html

      以下给出两段实例代码,也来自上述参考资料:

    1、hibernate中典型的ThreadLocal的应用,获取session;

    ...
    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;  
    }  
    

     2、

    public class Test {
        ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
        ThreadLocal<String> stringLocal = new ThreadLocal<String>();
     
         
        public void set() {
            longLocal.set(Thread.currentThread().getId());

                         //这里先获取每一个线程thread的ThreadLocalMap,然后map.set(this, value),this就是当前的ThreadLocal,value就是getId的值,也就为每个线程创建完副本,并存放入线程的ThreadLocalMap中,从而实现了线程本地变量的副本创建。

            stringLocal.set(Thread.currentThread().getName());
        }
         
        public long getLong() {
            return longLocal.get();
        }
         
        public String getString() {
            return stringLocal.get();
        }
         
        public static void main(String[] args) throws InterruptedException {
            final Test test = new Test();
             
             
            test.set();
            System.out.println(test.getLong());
            System.out.println(test.getString());
         
             
            Thread thread1 = new Thread(){
                public void run() {
                    test.set();
                    System.out.println(test.getLong());
                    System.out.println(test.getString());
                };
            };
            thread1.start();
            thread1.join();
             
            System.out.println(test.getLong());
            System.out.println(test.getString());
        }
    }
    

      

    ------------------------------------------------------------------------------------------终-----------------------------------------------------------------------------------------------------------------------------------------------

       

    详情移步:博客园-海子-http://www.cnblogs.com/dolphin0520/p/3920407.html

     
     
     
  • 相关阅读:
    cf3b(贪心)
    cf4b
    poj 1037(经典dp)
    网络流之SAP算法学习
    cf3d
    hdu 1572(dfs+最短路)
    hdu 1735(贪心)
    Elementary Methods in Number Theory Exercise 1.5.10
    Elementary Methods in Number Theory Exercise 1.5.12
    Elementary Methods in Number Theory Exercise 1.5.10
  • 原文地址:https://www.cnblogs.com/whtblog/p/8882340.html
Copyright © 2020-2023  润新知