理解java中的ThreadLocal


           

          一、對ThreadLocal概術

                     


          JDK API 寫道:

          該類提供了線程局部 (thread-local) 變量。這些變量不同于它們的普通對應物,因為訪問某個變量(通過其 get 或 set 方法)的每個線程都有自己的局部變量,它獨立于變量的初始化副本。ThreadLocal 實例通常是類中的 private static 字段,它們希望將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯。




          二、結合源碼理解



          可以看到ThreadLocal類中的變量只有這3int型:


              
          private final int threadLocalHashCode = nextHashCode();

            

              
          private static AtomicInteger nextHashCode = new AtomicInteger();

            

              
          private static final int HASH_INCREMENT = 0x61c88647;


              ThreadLocal實例的變量只有 threadLocalHashCode 
              ThreadLocal類的靜態變量nextHashCode 和HASH_INCREMENT
              實際上HASH_INCREMENT是一個常量,表示了連續分配的兩個ThreadLocal實例的 threadLocalHashCode值的增量,而nextHashCode 的表示了即將分配的下一個ThreadLocal實例的threadLocalHashCode 的值。

             而nextHashCode()方法就是將ThreadLocal類的下一個hashCode值即nextHashCode的值賦給實例的threadLocalHashCode,然后nextHashCode的值增加HASH_INCREMENT這個值。   

              private static int nextHashCode() {
              
          return nextHashCode.getAndAdd(HASH_INCREMENT); 
              }



             ThreadLocal有一個ThreadLocalMap靜態內部類,你可以簡單理解為一個MAP,這個‘Map’為每個線程復制一個變量的‘拷貝’存儲其中。

           看一下set()方法:  獲取當前線程的引用,從map中獲取該線程對應的map,如果map存在更新緩存值,否則創建并存儲
            


              
          public void set(T value) {
                  Thread t 
          = Thread.currentThread();
                  ThreadLocalMap map 
          = getMap(t);
                  
          if (map != null)
                      map.set(
          this, value);
                  
          else
                      createMap(t, value);
              }


           
           再來看一下get()方法: 首先獲取當前線程引用,以此為key去獲取響應的ThreadLocalMap,如果此‘Map’不存在則初始化一個,否則返回其中的變量。
            調用get方法如果此Map不存在首先初始化,創建此map,將線程為key,初始化的vlaue存入其中,注意此處的initialValue,我們可以覆蓋此方法,在首次調用時初始化一個適當的值,默認是null

           
              
          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();
              }

           


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

           


              
          protected T initialValue() {
                  
          return null;
              }




          我們來看下ThreadLocalMap靜態內部類,ThreadLocalMap 內部的Entry WeakReference

          static class ThreadLocalMap {

                

                  
          static class Entry extends WeakReference<ThreadLocal> {
                      
          /** The value associated with this ThreadLocal. */
                      Object value;

                      Entry(ThreadLocal k, Object v) 
          {
                          
          super(k);
                          value 
          = v;
                      }

                  }

          。。。。。

           
          ThreadLocal和多線程并發沒有什么關系。ThreadLocal模式是為了解決單線程內的跨類跨方法調用的
          ThreadLocal不是用來解決對象共享訪問問題的,而主要是提供了保持對象的方法和避免參數傳遞的方便的對象訪問方式。一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。各個線程中訪問的是不同的對象。





          三、例子




          引用Tim Cull的博文“SimpleDateFormat: Performance Pig”介紹下ThreadLocal的簡單使用,同時也對SimpleDateFormat的使用有個深入的了解。

           

          Tim Cull 寫道:

             Tim Cull碰到一個SimpleDateFormat帶來的嚴重的性能問題,該問題主要有SimpleDateFormat引發,創建一個 SimpleDateFormat實例的開銷比較昂貴,解析字符串時間時頻繁創建生命周期短暫的實例導致性能低下。即使將 SimpleDateFormat定義為靜態類變量,貌似能解決這個問題,但是SimpleDateFormat是非線程安全的,同樣存在問題,如果用 ‘synchronized’線程同步同樣面臨問題,同步導致性能下降(線程之間序列化的獲取SimpleDateFormat實例)。

              Tim Cull使用Threadlocal解決了此問題,對于每個線程SimpleDateFormat不存在影響他們之間協作的狀態,為每個線程創建一個SimpleDateFormat變量的拷貝或者叫做副本

          import java.text.DateFormat;  
          import java.text.ParseException;  
          import java.text.SimpleDateFormat;  
          import java.util.Date;  
          /** 
           * 使用ThreadLocal以空間換時間解決SimpleDateFormat線程安全問題。 
           * 
          @author  
           * 
           
          */
            
          public class DateUtil {  
                
              
          private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";  
                
              @SuppressWarnings(
          "rawtypes")  
              
          private static ThreadLocal threadLocal = new ThreadLocal() {  
                  
          protected synchronized Object initialValue() {  
                      
          return new SimpleDateFormat(DATE_FORMAT);  
                  }
            
              }
          ;  
            
              
          public static DateFormat getDateFormat() {  
                  
          return (DateFormat) threadLocal.get();  
              }
            
            
              
          public static Date parse(String textDate) throws ParseException {  
                  
          return getDateFormat().parse(textDate);  
              }
            
          }
           

          創建一個ThreadLocal類變量,這里創建時用了一個匿名類,覆蓋了initialValue方法,主要作用是創建時初始化實例。也可以采用下面方式創建

           

              //第一次調用get將返回null  
              private static ThreadLocal threadLocal = new ThreadLocal();  
              
          //獲取線程的變量副本,如果不覆蓋initialValue,第一次get返回null,故需要初始化一個SimpleDateFormat,并set到threadLocal中  
              public static DateFormat getDateFormat()   
              
          {  
                  DateFormat df 
          = (DateFormat) threadLocal.get();  
                  
          if(df==null){  
                      df 
          = new SimpleDateFormat(DATE_FORMAT)  
                      threadLocal.set(df);  
                  }
            
                  
          return df;  
              }
            


           

          posted on 2011-10-29 17:39 胡鵬 閱讀(1447) 評論(0)  編輯  收藏 所屬分類: java基礎J2EE

          導航

          <2011年10月>
          2526272829301
          2345678
          9101112131415
          16171819202122
          23242526272829
          303112345

          統計

          常用鏈接

          留言簿(3)

          隨筆分類

          隨筆檔案

          agile

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 宜良县| 綦江县| 临海市| 无锡市| 浏阳市| 宜都市| 西充县| 漳州市| 沙河市| 奉新县| 莱芜市| 汤原县| 天门市| 博爱县| 广饶县| 穆棱市| 顺昌县| 原平市| 革吉县| 襄城县| 米泉市| 田林县| 福安市| 昆明市| 棋牌| 津市市| 武功县| 枣庄市| 吴堡县| 巴马| 洛阳市| 大关县| 徐水县| 海阳市| 诏安县| 建昌县| 临猗县| 昌江| 凤山市| 盐池县| 双牌县|