posts - 262,  comments - 221,  trackbacks - 0

          關于ThreadLocal的用法,之前一直不太清楚,直到最近看了網上一篇文章《深入研究java.lang.ThreadLocal類》,再結合SUN的API,才對這個類有了一定的了解。

          ThreadLocal的核心思想很簡單:為每個獨立的線程提供一個變量的副本。

          我們知道在多線程的情況下,幾個線程同時訪問同一變量的情況很常見,Java提供的synchronized關鍵字使用了“同步鎖”的機制來阻止線程的競爭訪問,即“以時間換空間”。

          ThreadLocal則使用了“拷貝副本”的方式,人人有份,你用你的,我用我的,大家互不影響,是“以空間換時間”。每個線程修改變量時,實際上修改的是變量的副本,不怕影響到其它線程。

          ThreadLocal的一個最常見應用是為每個線程分配一個唯一的ID,例如線程ID,事務ID,一般保存在ThreadLocal中的變量都是很少需要修改的。

          為了加深對ThreadLocal的理解,下面我使用一個例子來演示ThreadLocal如何隔離線程間的變量訪問和修改:

          【1】SerialNum類

          package example.thread.threadLocal;

          public class SerialNum {

              
          private static int nextSerialNum = 1;

              @SuppressWarnings(
          "unchecked")
              
          private static ThreadLocal serialNum = new ThreadLocal() {
                  
          protected synchronized Object initialValue() {
                      return new Integer(nextSerialNum++);      

                  }                                                           
              };

              
          public static int get() {
                  
          return ((Integer) (serialNum.get())).intValue();
              }
              
              @SuppressWarnings(
          "unchecked")
              
          public static void set(Integer newSerial){
                  serialNum.set(newSerial);
              }
          }

          【2】GetSerialNumThread
          package example.thread.threadLocal;

          public class GetSerialNumThread implements Runnable {

              
          public static void main(String args[]) {

                  GetSerialNumThread serialNumGetter 
          = new GetSerialNumThread();
                  Thread t1 
          = new Thread(serialNumGetter, "Thread A");
                  Thread t2 
          = new Thread(serialNumGetter, "Thread B");
                  t1.start();
                  
          try {
                      t1.join();
                  } 
          catch (InterruptedException e) {
                      e.printStackTrace();
                  }    
                  t2.start();            
              }

              
          public void run() {
                  
          int mySerialNum = getSerialNum();
                  System.out.println("線程 " + Thread.currentThread().getName()
                          
          + " 獲取到的序列號是" + mySerialNum);
                  System.out.println(
          "線程 " + Thread.currentThread().getName()
                          
          + " 修改了序列號為" + (mySerialNum * 3));
                  setSerialNum(mySerialNum * 3);

                  System.out.println(
          "線程 " + Thread.currentThread().getName()
                          
          + " 再次獲得的序列號是" + getSerialNum());
              }

              
          private int getSerialNum() {
                  
          return SerialNum.get();
              }

              
          private void setSerialNum(int newSerialNum) {
                  SerialNum.set(
          new Integer(newSerialNum));
              }
          }

          運行的結果如下:
          線程 Thread A 獲取到的序列號是1
          線程 Thread A 修改了序列號為3
          線程 Thread A 再次獲得的序列號是3
          線程 Thread B 獲取到的序列號是2
          線程 Thread B 修改了序列號為6
          線程 Thread B 再次獲得的序列號是6

          可見第一個線程在調用SerialNum.set(int)方法修改static變量時,其實修改的是它自己的副本,而不是修改本地變量,第二個線程在初始化的時候拿到的序列號是2而不是7。

          為什么會這樣呢?明明serialNum是靜態變量啊?其實我們只需要看看ThreadLocal的內部構造就知道了:

          A. ThreadLocal的get()方法:
           /**
               * Returns the value in the current thread's copy of this thread-local
               * variable.  Creates and initializes the copy if this is the first time
               * the thread has called this method.
               *
               * 
          @return the current thread's value of this thread-local
               
          */
              
          public T get() {
                  Thread t 
          = Thread.currentThread();
                  ThreadLocalMap map 
          = getMap(t);
                  
          if (map != null)
                      
          return (T)map.get(this);

                  
          // Maps are constructed lazily.  if the map for this thread
                  
          // doesn't exist, create it, with this ThreadLocal and its
                  
          // initial value as its only entry.
                  T value = initialValue();
                  createMap(t, value);
                  
          return value;
              }

          B. ThreadLocal的set()方法:
          /**
               * Sets the current thread's copy of this thread-local variable
               * to the specified value.  Many applications will have no need for
               * this functionality, relying solely on the {
          @link #initialValue}
               * method to set the values of thread-locals.
               *
               * 
          @param value the value to be stored in the current threads' copy of
               *        this thread-local.
               
          */
              
          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在內部維護了一個Map,將變量的值和線程綁定起來,get/set方法都是對該線程對應的value進行操作,所以不會影響到其它線程。

          -------------------------------------------------------------
          生活就像打牌,不是要抓一手好牌,而是要盡力打好一手爛牌。
          posted on 2008-09-05 17:10 Paul Lin 閱讀(4756) 評論(1)  編輯  收藏 所屬分類: J2SE


          FeedBack:
          # re: 【原】Java 多線程 之 ThreadLocal
          2008-09-05 19:03 | 銀河使者
          在MyEclipse中開發基于hibernate的程序時,自動生成的Session工廠就用了ThreadLocal,以保證每一個線程只有一個Session。

          實際上,ThreadLocal的基本原理就是利用線程的id作為Map的key,將一個對象作為Map的value, 以保證一個線程只有一個key-value對。由于每一個線程有一個獨立的對象,而且每個線程的對象必須獨立,也就是說不能有其他的線程訪問當前線程的對象,這樣對于每個線程來說,是順序執行的,也就不可能發生象臟數據這樣的事情了。  回復  更多評論
            
          <2008年9月>
          31123456
          78910111213
          14151617181920
          21222324252627
          2829301234
          567891011

          常用鏈接

          留言簿(21)

          隨筆分類

          隨筆檔案

          BlogJava熱點博客

          好友博客

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 耒阳市| 读书| 台前县| 东城区| 吐鲁番市| 哈密市| 枣庄市| 自贡市| 石楼县| 上杭县| 景东| 宝应县| 进贤县| 嘉禾县| 祥云县| 华阴市| 叙永县| 驻马店市| 玉林市| 治县。| 奉贤区| 保亭| 松滋市| 永吉县| 达孜县| 鄂州市| 博湖县| 威远县| 贺州市| 读书| 滕州市| 新源县| 姜堰市| 南召县| 吴桥县| 满洲里市| 柘荣县| 桂东县| 长寿区| 湖南省| 揭西县|