java.util.concurrent包概覽

JDK8下的java.util.concurrent包

1. java.util.concurrent

1.1 併發容器

未完待續

1.2 線程池

java.util.concurrent包下的線程池相關類

常用線程池 ThreadPoolExecutor ForkJoinPool ScheduledThreadPoolExecutor

2. java.util.concurrent.locks

Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。此實現允許更靈活的結構,可以具有差別很大的屬性,可以支持多個相關的 Condition 對象。 

鎖是控制多個線程對共享資源進行訪問的工具。通常,鎖提供了對共享資源的獨佔訪問。一次只能有一個線程獲得鎖,對共享資源的所有訪問都需要首先獲得鎖。不過,某些鎖可能允許對共享資源併發訪問,如 ReadWriteLock 的讀取鎖。 

synchronized 方法或語句的使用提供了對與每個對象相關的隱式監視器鎖的訪問,但卻強制所有鎖獲取和釋放均要出現在一個塊結構中:當獲取了多個鎖時,它們必須以相反的順序釋放,且必須在與所有鎖被獲取時相同的詞法範圍內釋放所有鎖。 

雖然 synchronized 方法和語句的範圍機制使得使用監視器鎖編程方便了很多,而且還幫助避免了很多涉及到鎖的常見編程錯誤,但有時也需要以更爲靈活的方式使用鎖。例如,某些遍歷併發訪問的數據結果的算法要求使用 "hand-over-hand" 或 "chain locking":獲取節點 A 的鎖,然後再獲取節點 B 的鎖,然後釋放 A 並獲取 C,然後釋放 B 並獲取 D,依此類推。Lock 接口的實現允許鎖在不同的作用範圍內獲取和釋放,並允許以任何順序獲取和釋放多個鎖,從而支持使用這種技術。 

隨着靈活性的增加,也帶來了更多的責任。不使用塊結構鎖就失去了使用 synchronized 方法和語句時會出現的鎖自動釋放功能。在大多數情況下,應該使用以下語句: 

     Lock l = ...; 
     l.lock();
     try {
         // access the resource protected by this lock
     } finally {
         l.unlock();
     }
 鎖定和取消鎖定出現在不同作用範圍中時,必須謹慎地確保保持鎖定時所執行的所有代碼用 try-finally 或 try-catch 加以保護,以確保在必要時釋放鎖。 
Lock 實現提供了使用 synchronized 方法和語句所沒有的其他功能,包括提供了一個非塊結構的獲取鎖嘗試 (tryLock())、一個獲取可中斷鎖的嘗試 (lockInterruptibly()) 和一個獲取超時失效鎖的嘗試 (tryLock(long, TimeUnit))。 

Lock 類還可以提供與隱式監視器鎖完全不同的行爲和語義,如保證排序、非重入用法或死鎖檢測。如果某個實現提供了這樣特殊的語義,則該實現必須對這些語義加以記錄。 

注意,Lock 實例只是普通的對象,其本身可以在 synchronized 語句中作爲目標使用。獲取 Lock 實例的監視器鎖與調用該實例的任何 lock() 方法沒有特別的關係。爲了避免混淆,建議除了在其自身的實現中之外,決不要以這種方式使用 Lock 實例。 

除非另有說明,否則爲任何參數傳遞 null 值都將導致拋出 NullPointerException。 

內存同步
所有 Lock 實現都必須 實施與內置監視器鎖提供的相同內存同步語義,如 The Java Language Specification, Third Edition (17.4 Memory Model) 中所描述的: 

成功的 lock 操作與成功的 Lock 操作具有同樣的內存同步效應。 
成功的 unlock 操作與成功的 Unlock 操作具有同樣的內存同步效應。 
不成功的鎖定與取消鎖定操作以及重入鎖定/取消鎖定操作都不需要任何內存同步效果。 
實現注意事項
三種形式的鎖獲取(可中斷、不可中斷和定時)在其性能特徵、排序保證或其他實現質量上可能會有所不同。而且,對於給定的 Lock 類,可能沒有中斷正在進行的 鎖獲取的能力。因此,並不要求實現爲所有三種形式的鎖獲取定義相同的保證或語義,也不要求其支持中斷正在進行的鎖獲取。實現必需清楚地對每個鎖定方法所提供的語義和保證進行記錄。還必須遵守此接口中定義的中斷語義,以便爲鎖獲取中斷提供支持:完全支持中斷,或僅在進入方法時支持中斷。 

由於中斷通常意味着取消,而通常又很少進行中斷檢查,因此,相對於普通方法返回而言,實現可能更喜歡響應某個中斷。即使出現在另一個操作後的中斷可能會釋放線程鎖時也是如此。實現應記錄此行爲。

常用類有:Lock ReentrantLock ReadWriteLock ReentrantReadWriteLock Condition

ReentrantLock 說明

一個可重入的互斥鎖 Lock,它具有與使用 synchronized 方法和語句所訪問的隱式監視器鎖相同的一些基本行爲和語義,但功能更強大。 

ReentrantLock 將由最近成功獲得鎖,並且還沒有釋放該鎖的線程所擁有。當鎖沒有被另一個線程所擁有時,調用 lock 的線程將成功獲取該鎖並返回。如果當前線程已經擁有該鎖,此方法將立即返回。可以使用 isHeldByCurrentThread() 和 getHoldCount() 方法來檢查此情況是否發生。 

此類的構造方法接受一個可選的公平 參數。當設置爲 true 時,在多個線程的爭用下,這些鎖傾向於將訪問權授予等待時間最長的線程。否則此鎖將無法保證任何特定訪問順序。與採用默認設置(使用不公平鎖)相比,使用公平鎖的程序在許多線程訪問時表現爲很低的總體吞吐量(即速度很慢,常常極其慢),但是在獲得鎖和保證鎖分配的均衡性時差異較小。不過要注意的是,公平鎖不能保證線程調度的公平性。因此,使用公平鎖的衆多線程中的一員可能獲得多倍的成功機會,這種情況發生在其他活動線程沒有被處理並且目前並未持有鎖時。還要注意的是,未定時的 tryLock 方法並沒有使用公平設置。因爲即使其他線程正在等待,只要該鎖是可用的,此方法就可以獲得成功。 

建議總是 立即實踐,使用 lock 塊來調用 try,在之前/之後的構造中,最典型的代碼如下: 

 class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() { 
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }
 除了實現 Lock 接口,此類還定義了 isLocked 和 getLockQueueLength 方法,以及一些相關的 protected 訪問方法,這些方法對檢測和監視可能很有用。 

該類的序列化與內置鎖的行爲方式相同:一個反序列化的鎖處於解除鎖定狀態,不管它被序列化時的狀態是怎樣的。 

此鎖最多支持同一個線程發起的 2147483648 個遞歸鎖。試圖超過此限制會導致由鎖方法拋出的 Error。 

3. java.util.concurrent.atomic

類的小工具包,支持在單個變量上解除鎖的線程安全編程。事實上,此包中的類可將 volatile 值、字段和數組元素的概念擴展到那些也提供原子條件更新操作的類,其形式如下: 

  boolean compareAndSet(expectedValue, updateValue);
如果此方法(在不同的類間參數類型也不同)當前保持 expectedValue,則以原子方式將變量設置爲 updateValue,並在成功時報告 true。此包中的類還包含獲取並無條件設置值的方法,以及以下描述的較弱條件的原子更新操作 weakCompareAndSet。 

這些方法的規範使實現能夠使用當代處理器上提供的高效機器級別原子指令。但是在某些平臺上,該支持可能需要某種形式的內部鎖。因而,該方法不能嚴格保證不被阻塞 - 執行操作之前可能暫時阻塞線程。 

類 AtomicBoolean、AtomicInteger、AtomicLong 和 AtomicReference 的實例各自提供對相應類型單個變量的訪問和更新。每個類也爲該類型提供適當的實用工具方法。例如,類 AtomicLong 和 AtomicInteger 提供了原子增量方法。一個應用程序將按以下方式生成序列號: 

class Sequencer {
  private final AtomicLong sequenceNumber
    = new AtomicLong(0);
  public long next() {
    return sequenceNumber.getAndIncrement();
  }
}
原子訪問和更新的內存效果一般遵循以下可變規則,正如 The Java Language Specification, Third Edition (17.4 Memory Model) 中的聲明: 

get 具有讀取 volatile 變量的內存效果。 
set 具有寫入(分配)volatile 變量的內存效果。 
除了允許使用後續(但不是以前的)內存操作,其自身不施加帶有普通的非 volatile 寫入的重新排序約束,lazySet 具有寫入(分配)volatile 變量的內存效果。在其他使用上下文中,當爲 null 時(爲了垃圾回收),lazySet 可以應用不會再次訪問的引用。 
weakCompareAndSet 以原子方式讀取和有條件地寫入變量但不 創建任何 happen-before 排序,因此不提供與除 weakCompareAndSet 目標外任何變量以前或後續讀取或寫入操作有關的任何保證。 
compareAndSet 和所有其他的讀取和更新操作(如 getAndIncrement)都有讀取和寫入 volatile 變量的內存效果。 
除了包含表示單個值的類之外,此包還包含 Updater 類,該類可用於獲取任意選定類的任意選定 volatile 字段上的 compareAndSet 操作。AtomicReferenceFieldUpdater、AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater 是基於反射的實用工具,可以提供對關聯字段類型的訪問。它們主要用於原子數據結構中,該結構中同一節點(例如,樹節點的鏈接)的幾個 volatile 字段都獨立受原子更新控制。這些類在如何以及何時使用原子更新方面具有更大的靈活性,但相應的弊端是基於映射的設置較爲拙笨、使用不太方便,而且在保證方面也較差。 

AtomicIntegerArray、AtomicLongArray 和 AtomicReferenceArray 類進一步擴展了原子操作,對這些類型的數組提供了支持。這些類在爲其數組元素提供 volatile 訪問語義方面也引人注目,這對於普通數組來說是不受支持的。 

原子類也支持 weakCompareAndSet 方法,該方法具有受限制的適用性。在某些平臺上,弱版本在正常情況下可能比 compareAndSet 更有效,但不同的是 weakCompareAndSet 方法的任何給定調用可能意外 返回 false(即沒有明確的原因)。返回 false 僅意味着可以在需要時重新嘗試操作,具體取決於重複執行調用的保證,當該變量保持 expectedValue 並且沒有其他線程也在嘗試設置該變量時,最終將獲得成功。(例如,這樣的虛假失敗可能是由於內存爭用的結果,該爭用與期望值和當前值是否相等無關)。 此外,weakCompareAndSet 不提供通常需要同步控制的排序保證。但是,在這樣的更新與程序的其他 happen-before 排序不相關時,該方法可用於更新計數器和統計數據。當一個線程看到對 weakCompareAndSet 導致的原子變量的更新時,它不一定能看到在 weakCompareAndSet 之前發生的對任何其他 變量的更新。例如,在更新性能統計數據時,這也許可以接受,但其他情況幾乎不可以。 

AtomicMarkableReference 類將單個布爾值與引用關聯起來。例如,可以在數據結構內部使用此位,這意味着引用的對象在邏輯上已被刪除。AtomicStampedReference 類將整數值與引用關聯起來。例如,這可用於表示與更新系列對應的版本號。 

設計原子類主要用作各種構造塊,用於實現非阻塞數據結構和相關的基礎結構類。compareAndSet 方法不是鎖的常規替換方法。僅當對象的重要更新限定於單個 變量時才應用它。 

原子類不是 java.lang.Integer 和相關類的通用替換方法。它們不 定義諸如 hashCode 和 compareTo 之類的方法。(因爲原子變量是可變的,所以對於哈希表鍵來說,它們不是好的選擇。)另外,僅爲那些通常在預期應用程序中使用的類型提供類。例如,沒有表示 byte 的原子類。這種情況不常見,如果要這樣做,可以使用 AtomicInteger 來保持 byte 值,並進行適當的強制轉換。也可以使用 Float.floatToIntBits 和 Float.intBitstoFloat 轉換來保持 float 值,使用 Double.doubleToLongBits 和 Double.longBitsToDouble 轉換來保持 double 值。 

常用類有:AtomicInteger、AtomicLong、AtomicBoolean、AtomicReference<V>等等;

AtomicInteger方法摘要

 int get() 
          獲取當前值。 
 void set(int newValue) 
          設置爲給定值。 

 int incrementAndGet() 
          以原子方式將當前值加 1。 
 int decrementAndGet() 
          以原子方式將當前值減 1。 

 int addAndGet(int delta) 
          以原子方式將給定值與當前值相加。 

 int getAndDecrement() 
          以原子方式將當前值減 1。 
 int getAndIncrement() 
          以原子方式將當前值加 1。 

 int getAndAdd(int delta) 
          以原子方式將給定值與當前值相加。 

 int getAndSet(int newValue) 
          以原子方式設置爲給定值,並返回舊值。 


 void lazySet(int newValue) 
          最後設置爲給定值。 


 boolean compareAndSet(int expect, int update) 
          如果當前值 == 預期值,則以原子方式將該值設置爲給定的更新值。 

 boolean weakCompareAndSet(int expect, int update) 
          如果當前值 == 預期值,則以原子方式將該設置爲給定的更新值。 

位置

JDK8下的java.util.concurrent包

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章