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

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

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

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

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

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


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

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

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

          import java.util.Random;

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


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

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

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

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

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





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







          //注意其中的幾行注釋代碼
          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;
                  }

              }
            
            回復  更多評論
            
          # 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方法的源碼   回復  更多評論
            
          # 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;
              }


          }
            回復  更多評論
            
          主站蜘蛛池模板: 介休市| 会理县| 香港| 宿迁市| 正蓝旗| 绿春县| 通辽市| 达州市| 盐山县| 新泰市| 麦盖提县| 奈曼旗| 邳州市| 舞钢市| 喜德县| 庄浪县| 墨脱县| 鸡泽县| 扎兰屯市| 迁西县| 丹凤县| 盐边县| 扶绥县| 松潘县| 泰州市| 新闻| 浦县| 新巴尔虎右旗| 石渠县| 晋中市| 吉安县| 城固县| 建宁县| 独山县| 通道| 顺平县| 南通市| 巢湖市| 民权县| 勐海县| 长武县|