隨筆-204  評論-149  文章-0  trackbacks-0
          轉(zhuǎn):http://www.javaeye.com/post/504793?page=1

          首先,ThreadLocal 不是用來解決共享對象的多線程訪問問題的,一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。各個線程中訪問的是不同的對象。

          另外,說ThreadLocal使得各線程能夠保持各自獨立的一個對象,并不是通過ThreadLocal.set()來實現(xiàn)的,而是通過每個線程中的new 對象 的操作來創(chuàng)建的對象,每個線程創(chuàng)建一個,不是什么對象的拷貝或副本。通過ThreadLocal.set()將這個新創(chuàng)建的對象的引用保存到各線程的自己的一個map中,每個線程都有這樣一個map,執(zhí)行ThreadLocal.get()時,各線程從自己的map中取出放進去的對象,因此取出來的是各自自己線程中的對象,ThreadLocal實例是作為map的key來使用的。

          如果ThreadLocal.set()進去的東西本來就是多個線程共享的同一個對象,那么多個線程的ThreadLocal.get()取得的還是這個共享對象本身,還是有并發(fā)訪問問題。

          下面來看一個hibernate中典型的ThreadLocal的應(yīng)用:
           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    }

          可以看到在getSession()方法中,首先判斷當前線程中有沒有放進去session,如果還沒有,那么通過sessionFactory().openSession()來創(chuàng)建一個session,再將session set到線程中,實際是放到當前線程的ThreadLocalMap這個map中,這時,對于這個session的唯一引用就是當前線程中的那個ThreadLocalMap(下面會講到),而threadSession作為這個值的key,要取得這個session可以通過threadSession.get()來得到,里面執(zhí)行的操作實際是先取得當前線程中的ThreadLocalMap,然后將threadSession作為key將對應(yīng)的值取出。這個session相當于線程的私有變量,而不是public的。
          顯然,其他線程中是取不到這個session的,他們也只能取到自己的ThreadLocalMap中的東西。要是session是多個線程共享使用的,那還不亂套了。
          試想如果不用ThreadLocal怎么來實現(xiàn)呢?可能就要在action中創(chuàng)建session,然后把session一個個傳到service和dao中,這可夠麻煩的。或者可以自己定義一個靜態(tài)的map,將當前thread作為key,創(chuàng)建的session作為值,put到map中,應(yīng)該也行,這也是一般人的想法,但事實上,ThreadLocal的實現(xiàn)剛好相反,它是在每個線程中有一個map,而將ThreadLocal實例作為key,這樣每個map中的項數(shù)很少,而且當線程銷毀時相應(yīng)的東西也一起銷毀了,不知道除了這些還有什么其他的好處。

          總之,ThreadLocal不是用來解決對象共享訪問問題的,而主要是提供了保持對象的方法和避免參數(shù)傳遞的方便的對象訪問方式。歸納了兩點:
          1。每個線程中都有一個自己的ThreadLocalMap類對象,可以將線程自己的對象保持到其中,各管各的,線程可以正確的訪問到自己的對象。
          2。將一個共用的ThreadLocal靜態(tài)實例作為key,將不同對象的引用保存到不同線程的ThreadLocalMap中,然后在線程執(zhí)行的各處通過這個靜態(tài)ThreadLocal實例的get()方法取得自己線程保存的那個對象,避免了將這個對象作為參數(shù)傳遞的麻煩。


          當然如果要把本來線程共享的對象通過ThreadLocal.set()放到線程中也可以,可以實現(xiàn)避免參數(shù)傳遞的訪問方式,但是要注意get()到的是那同一個共享對象,并發(fā)訪問問題要靠其他手段來解決。但一般來說線程共享的對象通過設(shè)置為某類的靜態(tài)變量就可以實現(xiàn)方便的訪問了,似乎沒必要放到線程中。

          ThreadLocal的應(yīng)用場合,我覺得最適合的是按線程多實例(每個線程對應(yīng)一個實例)的對象的訪問,并且這個對象很多地方都要用到。
          posted on 2009-06-02 14:18 Frank_Fang 閱讀(415) 評論(3)  編輯  收藏 所屬分類: Java編程

          評論:
          # re: 正確理解ThreadLocal 2009-07-01 20:17 | Frank_Fang
          package test.thread;

          import java.util.Random;

          /**
           * ThreadLocal解決的是同一個線程內(nèi)的資源共享問題,而synchronized 解決的是多個線程間的資源共享問題,兩個問題沒有可比性
           * ThreadLocal的實現(xiàn)本來就比較簡單,只是用threadLocal變量來作為key來尋找本線程中所使用的一個實例,它解決的最主要的問題應(yīng)該就是減少同一個線程中的參數(shù)的傳遞。
           * 
           * 
           * 在同一個線程內(nèi),完全不相關(guān)的兩個段代碼、函數(shù)之間如何共享一個變量呢?通過ThreadLocal可以做到 
           * 而且這兩段代碼之間不用顯式的傳遞參數(shù),降低了耦合
           * 
           * ThreadLocal類似一個 Thread Context,減少調(diào)用、傳參復(fù)雜度,增加環(huán)境依賴。
           * 
           * ThreadLocal解決的是同一個線程內(nèi)的資源共享問題,而synchronized 解決的是多個線程間的資源共享問題,兩個問題沒有可比性。
           * 同一個線程內(nèi)的資源本來就是共享的,只是增加了使用的方便性,避免通過方法傳遞參數(shù)就是他的優(yōu)點!
           *
           
          */


          /**
           * 如果一個類中定義了一個static的ThreadLocal,一個共享對象可以通過該ThreadLocal的set設(shè)置到多個線程的ThreadLocalMap中,但是這多個線程的ThreadLocalMap中存著的僅僅是該對象的引用,指向那個共享對象,而不是什么副本,通過ThreadLocal的get方法取到的是就是那個共享對象本身,共享訪問安全問題還是要靠其他方法來解決。而實際中是不會這樣使用的,很顯然,這個共享變量是需要同步的(如果是線程之間的共享對象,那么其引用根本沒有必要放在線程中,需要同步) 

          ThreadLocalMap在每個線程中有一個,而不是存在于ThreadLocal中,ThreadLocal更多是作為一個工具類,里面只包含一個int threadLocalHashCode,而不包含其他任何數(shù)據(jù),數(shù)據(jù)是放在每個線程的ThreadLocalMap中的,里面存放的是你要通過ThreadLocal進行set和get的對象(引用),threadLocalHashCode相當于這個Map的key。 

          如果一個類中定義了多個ThreadLocal,那么這些個ThreadLocal中的threadLocalHashCode值是不同的,也就是key不同,所以可以用來將不同的多個對象放到線程中。 

          考慮一個類的多線程環(huán)境,對于該類中的static的某個ThreadLocal對象,在多個線程中是同一個對象,同一個threadLocalHashCode值,也就是同一個key,但是不同的是每個線程中的ThreadLocalMap,每個線程都有自己的ThreadLocalMap,所以相同的key可以對應(yīng)不同的對象。 

          說到底,ThreadLocal的作用就是將經(jīng)常要用到的對象的引用放到屬于線程自己的一個存儲空間中,在該線程的執(zhí)行過程中,可以通過類的靜態(tài)的ThreadLocal來方便的獲取到這個對象,而不用通過參數(shù)的形式傳來傳去。 
           
          */





          /**
           * 首先要能清楚為什么要使用ThreadLocal,如果沒有ThreadLocal,能不能解決問題。
          沒有ThreadLocal的話,每個Thread中都有輸入自己的一個本地變量,但是在整個Thread的生命中,如果要穿梭很多class的很多method來使用這個本地變量的話,就要一直一直向下傳送這個變量,顯然很麻煩。
          那么怎么才能在這個Thread的生命中,在任何地方都能夠方便的訪問到這個變量呢,這時候ThreadLocal就誕生了。
          ThreadLocal就是這么個作用,除此之外和通常使用的本地變量沒有任何區(qū)別。
          我奇怪的是為什么非要和synchronized扯上關(guān)系,完全風馬牛不相及的兩個東西。
           
          */







          //注意其中的幾行注釋代碼
          public class ThreadLocalDemo implements Runnable {   
              
              
                 
          private final static  ThreadLocal studentLocal = new ThreadLocal();   
                 
                 
          private Student classStudent = new Student();
                    
                 
          public static void main(String[] agrs) {   
                     ThreadLocalDemo td 
          = new ThreadLocalDemo();  
                     
                     
          //td中的classStudent為多個線程共享了的
                       Thread t1 = new Thread(td,"a");   
                       Thread t2 
          = new Thread(td,"b");   
                         
                      t1.start();   
                      t2.start();   
                        
                        
                
                
                    }
             
                    
                  
          /* (non-Javadoc)  
                   * @see java.lang.Runnable#run()  
                   
          */
            
                  
          public void run() {   
                       accessStudent();   
                  }
             
                
                  
          public  void  accessStudent() {   
                         
                      String currentThreadName 
          = Thread.currentThread().getName();   
                      System.out.println(currentThreadName
          +" is running!");   
                      Random random 
          = new Random();   
                      
          int age = random.nextInt(100);   
                      System.out.println(
          "thread "+currentThreadName +" set age to:"+age);   
                      
                      
          //Student student = getStudentNotThreadLocal();
                      Student student = getStudent();   
                      student.setAge(age);   
                      System.out.println(
          "thread "+currentThreadName+" first  read age is:"+student.getAge()+"-----"+student);   
                      
          try {   
                      Thread.sleep(
          5000);   
                      }
             
                      
          catch(InterruptedException ex) {   
                          ex.printStackTrace();   
                      }
             
                      System.out.println(
          "thread "+currentThreadName +" second read age is:"+student.getAge()+"-----"+student);   
                         
                  }
             
                     
                  
          protected Student getStudent() {   
                      Student student 
          = (Student)studentLocal.get();   
                      
          if(student == null{   
                          student 
          = new Student();
                          
          //student = classStudent;
                          studentLocal.set(student);   
                      }
             
                      
          return student;   
                  }
             
                     
                  
          protected void setStudent(Student student) {   
                      studentLocal.set(student);   
                  }

                  
                  
          protected Student getStudentNotThreadLocal(){
                      Student student 
          = new Student();
                      
          return student;
                  }

              }
            
            回復(fù)  更多評論
            
          # re: 正確理解ThreadLocal 2009-07-01 20:27 | Frank_Fang
              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();
              }


              
          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的get和set方法的源碼   回復(fù)  更多評論
            
          # re: 正確理解ThreadLocal 2009-07-01 20:30 | Frank_Fang
          package edu.bupt.vo;

          import org.hibernate.HibernateException;
          import org.hibernate.Session;
          import org.hibernate.cfg.Configuration;

          /**
           * Configures and provides access to Hibernate sessions, tied to the
           * current thread of execution.  Follows the Thread Local Session
           * pattern, see {
          @link http://hibernate.org/42.html }.
           
          */

          public class HibernateSessionFactory {

              
          /** 
               * Location of hibernate.cfg.xml file.
               * Location should be on the classpath as Hibernate uses  
               * #resourceAsStream style lookup for its configuration file. 
               * The default classpath location of the hibernate config file is 
               * in the default package. Use #setConfigFile() to update 
               * the location of the configuration file for the current session.   
               
          */

              
          private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
              
          private static final ThreadLocal<Session> threadLocal = new ThreadLocal<Session>();
              
          private  static Configuration configuration = new Configuration();    
              
          private static org.hibernate.SessionFactory sessionFactory;
              
          private static String configFile = CONFIG_FILE_LOCATION;

              
          static {
                  
          try {
                      configuration.configure(configFile);
                      sessionFactory 
          = configuration.buildSessionFactory();
                  }
           catch (Exception e) {
                      System.err
                              .println(
          "%%%% Error Creating SessionFactory %%%%");
                      e.printStackTrace();
                  }

              }

              
          private HibernateSessionFactory() {
              }

              
              
          /**
               * Returns the ThreadLocal Session instance.  Lazy initialize
               * the <code>SessionFactory</code> if needed.
               *
               *  
          @return Session
               *  
          @throws HibernateException
               
          */

              
          public static Session getSession() throws HibernateException {
                  Session session 
          = (Session) threadLocal.get();

                  
          if (session == null || !session.isOpen()) {
                      
          if (sessionFactory == null{
                          rebuildSessionFactory();
                      }

                      
          //sessionFactory.openSession()是new 了一個新的session
                      session = (sessionFactory != null? sessionFactory.openSession()
                              : 
          null;
                      threadLocal.set(session);
                  }


                  
          return session;
              }


              
          /**
               *  Rebuild hibernate session factory
               *
               
          */

              
          public static void rebuildSessionFactory() {
                  
          try {
                      configuration.configure(configFile);
                      sessionFactory 
          = configuration.buildSessionFactory();
                  }
           catch (Exception e) {
                      System.err
                              .println(
          "%%%% Error Creating SessionFactory %%%%");
                      e.printStackTrace();
                  }

              }


              
          /**
               *  Close the single hibernate session instance.
               *
               *  
          @throws HibernateException
               
          */

              
          public static void closeSession() throws HibernateException {
                  Session session 
          = (Session) threadLocal.get();
                  threadLocal.set(
          null);

                  
          if (session != null{
                      session.close();
                  }

              }


              
          /**
               *  return session factory
               *
               
          */

              
          public static org.hibernate.SessionFactory getSessionFactory() {
                  
          return sessionFactory;
              }


              
          /**
               *  return session factory
               *
               *    session factory will be rebuilded in the next call
               
          */

              
          public static void setConfigFile(String configFile) {
                  HibernateSessionFactory.configFile 
          = configFile;
                  sessionFactory 
          = null;
              }


              
          /**
               *  return hibernate configuration
               *
               
          */

              
          public static Configuration getConfiguration() {
                  
          return configuration;
              }


          }
            回復(fù)  更多評論
            
          主站蜘蛛池模板: 肇庆市| 宁陵县| 益阳市| 尼玛县| 托里县| 凌海市| 临沧市| 巩留县| 济宁市| 阳泉市| 吴江市| 宝丰县| 襄樊市| 永城市| 从化市| 伊川县| 淮南市| 阳高县| 昌江| 观塘区| 水城县| 九江县| 女性| 原阳县| 宜兰市| 陆丰市| 体育| 于都县| 营口市| 微山县| 普兰县| 乌拉特中旗| 永顺县| 五台县| 延津县| 佛冈县| 衡东县| 襄樊市| 红安县| 永仁县| 罗定市|