Java中ThreadLocal的設計與使用

jopen 11年前發布 | 17K 次閱讀 Java Java開發

ThreadLocal是什么

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

ThreadLocal的設計

首先看看ThreadLocal的接口: 

    Object get() ; // 返回當前線程的線程局部變量副本 protected Object initialValue(); // 返回該線程局部變量的當前線程的初始值 
    void set(Object value); // 設置當前線程的線程局部變量副本的值 

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

  protected Object initialValue() { return null; } 

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

  public class ThreadLocal 
  { 
   private Map values = Collections.synchronizedMap(new HashMap()); 
   public Object get() 
   { 
   Thread curThread = Thread.currentThread(); 
   Object o = values.get(curThread); 
   if (o == null && !values.containsKey(curThread)) 
   { 
    o = initialValue(); 
    values.put(curThread, o); 
   } 
   return o; 
   } 

   public void set(Object newValue) 
   { 
   values.put(Thread.currentThread(), newValue); 
   } 

   public Object initialValue() 
   { 
   return null; 
   } 
  } 


  當然,這并不是一個工業強度的實現,但JDK中的ThreadLocal的實現總體思路也類似于此。 

   ThreadLocal的使用 

   如果希望線程局部變量初始化其它值,那么需要自己實現ThreadLocal的子類并重寫該方法,通常使用一個內部匿名類對ThreadLocal進行子類化,比如下面的例子,SerialNum類為每一個類分配一個序號: 

  public class SerialNum 
  { 
   // The next serial number to be assigned 

   private static int nextSerialNum = 0; 
   private static ThreadLocal serialNum = new ThreadLocal() 
   { 
   protected synchronized Object initialValue() 
   { 
    return new Integer(nextSerialNum++); 
   } 
   }; 

   public static int get() 
   { 
   return ((Integer) (serialNum.get())).intValue(); 
   } 
  } 


  SerialNum類的使用將非常地簡單,因為get()方法是static的,所以在需要獲取當前線程的序號時,簡單地調用: 

  int serial = SerialNum.get(); 

  即可。 

   在線程是活動的并且ThreadLocal對象是可訪問的時,該線程就持有一個到該線程局部變量副本的隱含引用,當該線程運行結束后,該線程擁有的所以線程局部變量的副本都將失效,并等待垃圾收集器收集。 

   ThreadLocal與其它同步機制的比較 

    ThreadLocal和其它同步機制相比有什么優勢呢?ThreadLocal和其它所有的同步機制都是為了解決多線程中的對同一變量的訪問沖突,在普 通的同步機制中,是通過對象加鎖來實現多個線程對同一變量的安全訪問的。這時該變量是多個線程共享的,使用這種同步機制需要很細致地分析在什么時候對變量 進行讀寫。

原文地址:http://www.jcwcn.com/article-30276-1.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!