Guava-Concurrency

概述

在這篇文章中涉及到以下知識點:

  • Monitor類充當Mutex使用,用來確保對代碼進行串行訪問,非常類似於synchronized關鍵字,但語義更加簡單,並且有一些有用的附加功能。
  • ListenableFuture類的功能與Listenable類在Java中的功能相同,但我們可以註冊一個回調方法,以便在Future本身完成之後運行。
  • FutureCallback類允許我們訪問Future task的結果,並且允許我們針對成功或失敗的場景進行處理。
  • SettableFutureAsyncFunctionFutureFallback類是我們在處理Future實例和進行對象異步轉換時非常有用的實用工具類。
  • RateLimiter類是用來限制線程訪問資源的頻率,它非常類似於一個信號量(semaphore),但是RateLimiter類不是通過線程總數限制訪問,而是根據時間限制訪問。

Synchronizing threads

雖然Java提供了在程序中運行多個線程的能力,但是有時我們需要限制訪問(同步),以便在任何給定的時間只有一個線程可以訪問代碼的一部分。Java提供了synchronized關鍵字來實現串行訪問的目標。但是使用synchronized有一些問題。首先,如果我們需要在線程上調用wait(),我們必須記住使用while循環:

while(someCondition){
     try {
        wait();
     } catch (InterruptedException e) {
       //In this case we don't care, but we may want
       //to propagate with Thread.interrupt()
     }
}

其次,如果我們有多個條件可以導致線程進入等待狀態,那麼我們必須調用notifyAll(),因爲我們無法針對特定的條件通知線程。使用notifyAll()而不是notify()是不太理想的,因爲它會喚醒所有線程來爭奪鎖,而最終只有一個線程會獲得鎖。Java 5引入了ReentrantLock類和創建Condition的能力。我們可以通過使用ReentrantLock.newCondition()方法來實現更細粒度控制。現在可以用Condition.signal()調用(類似於notify())喚醒等待特定條件發生的單個線程,儘管有一個Condition.signalAll()方法具有與調用notifyAll()相同的抖動效果。但是我們仍然有一些違反直覺的while循環需要處理:

while(list.isEmpty()){
	Condition.await();
}

幸運的是,Guava有解決這個問題的辦法----Monitor

Monitor

針對上面的問題,Guava的Monitor類爲我們提供了一種解決方案。它允許多種conditions,並且通過從顯式通知系統切換到隱式通知系統,完全消除了通知所有線程的可能性。讓我們來看一個例子:

 public class MonitorSample {
       private List<String> list = new ArrayList<String>();
       private static final int MAX_SIZE = 10;
       //創建Monitor實例
       private Monitor monitor = new Monitor();
       //利用Monitor實例構造Guard實例。
       private Monitor.Guard listBelowCapacity = new Monitor.Guard(monitor) {
       	//Guard類有一個抽象的isSatisfied方法,返回值爲boolean。(這兒表示如果集合的大小小於10那麼就返回true)
           @Override
           public boolean isSatisfied() {
               return list.size() < MAX_SIZE;
           }
	   };
       public void addToList(String item) throws InterruptedException {
       //只有在滿足 Guard condition evaluates to true 的時候線程才允許進入
       //enterWhen方法允許線程在滿足Guard condition時進入代碼塊。另外值得注意的是,我們並沒有顯式地發送任何線程信號;它完全隱含在被滿足的Guard condition中。
           monitor.enterWhen(listBelowCapacity);
           try {
               list.add(item);
           } finally {
           //離開時需要釋放monitor
               monitor.leave();
           }
	   } 
}

Monitor explained

當一個線程進入一個Monitor塊時,它被認爲佔用了該Monitor實例,並且一旦該線程離開,它就不再佔用Monitor塊。任何時候只有一個線程可以進入Monitor塊。該語義與使用synchronized或ReentrantLocks相同;如果一個線程已經進入Monitor塊,那麼其他線程必須等到當前線程釋放鎖,或者通過monitor.leave離開監視器塊才能進入Monitor塊。同一個線程可以任意次數地進出同一個Monitor塊,但是每次進入後面必須跟着一個離開。

Monitor best practice

對於Monitor返回boolean值的方法應該始終被使用在包含包含try/finally塊的 if 語句中,以確保線程始終能夠退出Monitor塊。

if (monitor.enterIf(guardCondition)) {
    try {
        doWork();
    } finally {
        monitor.leave();
    }
}

對於不返回任何值的Monitor方法,方法的調用應該緊跟着try/finally塊。

monitor.enterWhen(guardCondition);
try {
     doWork();
} finally {
    monitor.leave()
}

ListenableFuture

FutureCallback

SettableFuture

AsyncFunction

FutureFallback

Futures

RateLimiter

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