zoukankan      html  css  js  c++  java
  • ThreadLocal简单学习

    学习Struts2时,遇到ThreadLocal,不甚理解,所以对此作了一番学习。

    ThreadLocal是什么呢?首先这并不是一个线程类,它是用来提供本地线程数据,也就是它保存的数据是线程相关的,每个线程都有一份数据副本,一个线程可以对其保存的副本进行修改,却不会影响其它的线程中的数据。这样说不易理解,比较这三种情况的数据,1、非ThreadLocal类型的静态成员数据;2、非ThreadLocal类型的非静态成员数据;3、ThreadLocal类型的静态数据。第一种情况中的数据是类类型的,所以此变量是所有线程共享的,第二种情况中的数据是实例对象相关的数据,是线程相关的,不同线程中实例数据的修改是相互不影响的。第三种虽然是静态类型的数据,但是由于是通过ThreadLocal保存的,所以还是线程相关的。由第三种类型的数据和第一种类型的数据之间的对比,可以帮助了解ThreadLocal的功能。

    ThreadLocal的接口功能比较简单,常用的为get、set、remove,它们的函数签名如下
    get接口public T get();
    set接口public void set(T);
    remove接口public void remove();

    下面用一个简单的项目来说明ThreadLocal的功能。项目内容很简单,主要包括三个类TestThread、Main和TestClass
    TestClass.java代码如下
    import  java.util.Random;


    public   class  TestClass {
             private   static  int   value =0;
             public   int  nonStaticValue  ;
             private   static  ThreadLocal<Integer>  threadLocalValue =  new  ThreadLocal<Integer>();
             public  TestClass(){
                  Random rand=  new  Random();
                    nonStaticValue  =rand.nextInt(100);
                    value  ++;
           }
           
             public   static  Integer getLocalThreadValue(){
                    return   threadLocalValue  .get();
           }
             public   static  void  setLocalThreadValue(Integer val){
                    threadLocalValue  .set(val);
           }
           
             public   static  int  getStaticValue(){
                    return   value  ;
           }
             public   int  getNonStaticValue(){
                    return   nonStaticValue  ;
           }
    }
    TestThread.java代码如下
    public   class  TestThread  extends  Thread {
             private   int  id  ;
           TestClass  clz ;

             public  TestThread(  int  id) {
                    super  ( "thread-"  +id);
                    this  . id  = id;
                  System.  out  .println( "thread in test thread constructor "
                               + Thread. currentThread().getName());
           }
             @Override
             public   void  run() {
                  TestClass. setLocalThreadValue( id);
                               clz  =  new  TestClass();
                    int  maxRuntime=2000;
                    int  step=1000;
                    int  totalRuntime=0;
                    while  (totalRuntime<maxRuntime) {
                         totalRuntime+=step;
                         System.  out  .println(Thread.currentThread().getName() +  "[non-static-value:"
                                      +  clz .getNonStaticValue() +  ",static-value:"
                                      + TestClass. getStaticValue() +  ",thread local value:"
                                      + TestClass. getLocalThreadValue() +  "]" );
                           try  {
                               Thread. sleep(2000);
                         }  catch  (InterruptedException e) {
                               e.printStackTrace();
                         }
                  }
           }
    }
    Main.java代码如下

    public   class  Main  extends  Thread{

             public   int  num  ;
             public  Main(String name,  int  num){
                    super  (name);
                    this  . num  =num;
           }
             @Override
             public   void  run(){
                    for  ( int  i=0;i<3;i++){
                           try  {
                               Thread. sleep(1000);
                         }  catch  (InterruptedException e) {
                               e.printStackTrace();
                         }
                           num ++;
                         System.  out  .println(Thread.currentThread().getName()+  "[ num = " +  num +  " ]" );
                  }
           }
             public   static  void  main(String[] args) {
                    int  num=3;
                  TestThread[] threads=  new  TestThread[num];
                    for  ( int  i=0;i<num;i++){
                         threads[i]=  new  TestThread(i);
                  }
                    for  ( int  i=0;i<num;i++){
                         threads[i].start();
                  }
                  System.  out  .println( "--------------------------------------+\t\r"  );
                  
                  num=2;
                  Main[] mains=  new  Main[num];
                    for  ( int  i=0;i<num;i++){
                         mains[i]=  new  Main( "main thread"  +i,i);
                  }
                    for  ( int  i=0;i<num;i++)
                         mains[i].start();
           }

    }
    运行之后的输出结果如下
    thread in test thread constructor main
    thread in test thread constructor main
    thread in test thread constructor main
    --------------------------------------   

    thread-0[non-static-value:26,static-value:3,thread local value:0]
    thread-1[non-static-value:49,static-value:3,thread local value:1]
    thread-2[non-static-value:59,static-value:3,thread local value:2]
    main thread0[ num = 1 ]
    main thread1[ num = 2 ]
    thread-0[non-static-value:26,static-value:3,thread local value:0]
    thread-1[non-static-value:49,static-value:3,thread local value:1]
    thread-2[non-static-value:59,static-value:3,thread local value:2]
    main thread0[ num = 2 ]
    main thread1[ num = 3 ]
    main thread0[ num = 3 ]
    main thread1[ num = 4 ]

    看其中的输出,非静态的成员变量(私有或公有)是线程相关的,静态的类变量是多个线程共享的,而由静态的ThreadLocal对象保存的数据,依旧是线程相关的,各个线程之间的值是有区别的。

    [ThreadLocal的实现原理]
    要理解ThreadLocal的实现原理,需要了解ThreadLocalMap类。TThreadLocalMap有一个Entry类型的数组,每个Entry是一个<ThreadLocal,Object>的键值对,在数组中的索引由ThreadLocal对象的哈希值和当前数组的长度值减一做与操作而来,如果出现冲突,就往后查找。ThreadLocalMap类中插入键值对数据的set方法如下所示

             private   void  set(ThreadLocal key, Object value) {
                Entry[] tab = table;
                 int  len = tab.length;
                 int  i = key.threadLocalHashCode & (len-1); //首先获取数组索引值
                /*
                                  从计算出来的索引点开始比较,如果能找到相应的key,就更新值
                         */
                 for  (Entry e = tab[i];
                     e !=  null  ;
                     e = tab[i = nextIndex(i, len)]) {
                    ThreadLocal k = e.get();

                     if  (k == key) {
                        e.value = value;
                         return  ;
                    }

                     if  (k ==  null  ) {
                        replaceStaleEntry(key, value, i);
                         return  ;
                    }
                }

                tab[i] =  new  Entry(key, value); //没有找到相应的key,插入新数据
                 int  sz = ++size;
                 if  (!cleanSomeSlots(i, sz) && sz >= threshold)
                    rehash();
            }

    每个Thread类型的线程都有个ThreadLocalMap类型的实例变量threadlocals,用来存放与此线程相关的ThreadLocal变量。再来看ThreadLocal保存数据的set方法,代码如下
       public   void  set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
             if  (map !=  null  )
                map.set(  this  , value);
             else
                createMap(t, value);
        }
    首先获取当前的线程t,由getMap方法获取当前线程的threadLocals属性map,然后保存键值对。在保存的时候采用this对象做为键,同样在获取数据的时候,也是以this作为键来获取相应的数据。因为一个线程可能有多个由ThreadLocal对象保存的变量,采用this作为key,可以实现这样不同ThreadLocal对象之间的区分。
  • 相关阅读:
    关于注解
    关于泛型
    关于ER图和UML图之间的对比
    关于Eclipse中的egit的常规使用和模板
    关于Eclipse中的开源框架EMF(Eclipse Modeling Framework),第三部分
    关于Eclipse Modeling Framework进行建模,第二部分
    SQL Server 2005/2008备份数据库时提示“无法打开备份设备”
    试用版SQL Server 2008 R2 提示评估期已过
    该登录名来自不受信任的域,不能与 Windows 身份验证一起使用。
    SVN-如何删除 SVN 文件夹下面的小图标
  • 原文地址:https://www.cnblogs.com/javawebsoa/p/3034421.html
Copyright © 2011-2022 走看看