java類ThreadLocal的理解

ThreadLocal是什麼 

    ThreadLocal是什麼呢?其實ThreadLocal並不是是一個線程的本地實現版本,它並非一個Thread,而是thread local variable(線程局部變量)。也許把它命名爲ThreadLocalVar更加合適。線程局部變量(ThreadLocal)其實的功用很是簡單,就是爲每個使用該變量的線程都提供一個變量值的副本,是每個線程均可以獨立地改變本身的副本,而不會和其它線程的副本衝突。從線程的角度看,就好像每個線程都徹底擁有該變量。線程局部變量並非Java的新發明,在其它的一些語言編譯器實現(如IBM XL FORTRAN)中,它在語言的層次提供了直接的支持。由於Java中沒有提供在語言層次的直接支持,而是提供了一個ThreadLocal的類來提供支持,因此,在Java中編寫線程局部變量的代碼相對比較笨拙,這也許是線程局部變量沒有在Java中獲得很好的普及的一個緣由吧。 


ThreadLocal的設計 

首先看看ThreadLocal的接口: 

Java代碼    收藏代碼
  1. Object get() ; // 返回當前線程的線程局部變量副本    
  2. protected  Object initialValue(); // 返回該線程局部變量的當前線程的初始值   
  3. void set(Object value); // 設置當前線程的線程局部變量副本的值   



ThreadLocal有3個方法,其中值得注意的是initialValue(),該方法是一個protected的方法,顯然是爲了子類重寫而特地實現的。該方法返回當前線程在該線程局部變量的初始值,這個方法是一個延遲調用方法,在一個線程第1次調用get()或者set(Object)時才執行,而且僅執行1次。ThreadLocal中的確實實現直接返回一個null: 

Java代碼    收藏代碼
  1. protected Object initialValue() {   
  2.     return null;   
  3. }   



    ThreadLocal是如何作到爲每個線程維護變量的副本的呢?其實實現的思路很簡單,在ThreadLocal類中有一個Map,用於存儲每個線程的變量的副本。好比下面的示例實現: 


Java代碼    收藏代碼
  1. public class ThreadLocal {    
  2.   private Map values = Collections.synchronizedMap(new HashMap());   
  3.   
  4.   public Object get() {   
  5.     Thread curThread = Thread.currentThread();   
  6.     Object o = values.get(curThread);   
  7.     if (o == null && !values.containsKey(curThread)) {   
  8.       o = initialValue();   
  9.       values.put(curThread, o);   
  10.     }   
  11.     return o;   
  12.   }   
  13.   
  14.   public void set(Object newValue) {   
  15.     values.put(Thread.currentThread(), newValue);   
  16.   }   
  17.   
  18.   public Object initialValue() {   
  19.     return null;   
  20.   }   
  21. }   


固然,這並非一個工業強度的實現,但JDK中的ThreadLocal的實現整體思路也相似於此。 


ThreadLocal的使用 

若是但願線程局部變量初始化其它值,那麼須要本身實現ThreadLocal的子類並重寫該方法,一般使用一個內部匿名類對ThreadLocal進行子類化,好比下面的例子,SerialNum類爲每個類分配一個序號: 


Java代碼    收藏代碼
  1. public class SerialNum {   
  2.      // The next serial number to be assigned   
  3.      private static int nextSerialNum = 0;   
  4.     
  5.      private static ThreadLocal serialNum = new ThreadLocal() {   
  6.          protected synchronized Object initialValue() {   
  7.              return new Integer(nextSerialNum++);   
  8.          }   
  9.      };   
  10.     
  11.      public static int get() {   
  12.          return ((Integer) (serialNum.get())).intValue();   
  13.      }   
  14.  }   



SerialNum類的使用將很是地簡單,由於get()方法是static的,因此在須要獲取當前線程的序號時,簡單地調用: 
Java代碼    收藏代碼
  1. int serial = SerialNum.get();   
便可。  在線程是活動的而且ThreadLocal對象是可訪問的時,該線程就持有一個到該線程局部變量副本的隱含引用,當該線程運行結束後,該線程擁有的因此線程局部變量的副本都將失效,並等待垃圾收集器收集。  ThreadLocal與其它同步機制的比較      ThreadLocal和其它同步機制相比有什麼優點呢?ThreadLocal和其它全部的同步機制都是爲了解決多線程中的對同一變量的訪問衝突,在普通的同步機制中,是經過對象加鎖來實現多個線程對同一變量的安全訪問的。這時該變量是多個線程共享的,使用這種同步機制須要很細緻地分析在何時對變量進行讀寫,何時須要鎖定某個對象,何時釋放該對象的鎖等等不少。全部這些都是由於多個線程共享了資源形成的。ThreadLocal就從另外一個角度來解決多線程的併發訪問,ThreadLocal會爲每個線程維護一個和該線程綁定的變量的副本,從而隔離了多個線程的數據,每個線程都擁有本身的變量副本,從而也就沒有必要對該變量進行同步了。ThreadLocal提供了線程安全的共享對象,在編寫多線程代碼時,能夠把不安全的整個變量封裝進ThreadLocal,或者把該對象的特定於線程的狀態封裝進ThreadLocal。      因爲ThreadLocal中能夠持有任何類型的對象,因此使用ThreadLocal get當前線程的值是須要進行強制類型轉換。但隨着新的Java版本(1.5)將模版的引入,新的支持模版參數的ThreadLocal<T>類將從中受益。也能夠減小強制類型轉換,並將一些錯誤檢查提早到了編譯期,將必定程度地簡化ThreadLocal的使用。  總結      固然ThreadLocal並不能替代同步機制,二者面向的問題領域不一樣。同步機制是爲了同步多個線程對相同資源的併發訪問,是爲了多個線程之間進行通訊的有效方式;而ThreadLocal是隔離多個線程的數據共享,從根本上就不在多個線程之間共享資源(變量),這樣固然不須要對多個線程進行同步了。因此,若是你須要進行多個線程之間進行通訊,則使用同步機制;若是須要隔離多個線程之間的共享衝突,可使用ThreadLocal,這將極大地簡化你的程序,使程序更加易讀、簡潔。  爲何轉載它呢?  我看了好多,其餘的都是重複的,並且基本上都是說的ThreadLoacal的內部實現,我以爲這篇比較好,講了其與同步的區別:  我的認爲就是ThreadLocal是讓不一樣的線程同時(能夠說同時,雖然其實是不能徹底同時進行的)訪問的是不一樣的變量,而同步是讓不一樣的線程在不一樣的時間內反問一樣的變量。
相關文章
相關標籤/搜索