【Java】【非同期】CountDownLatch ~カウントダウンラッチ~

■ CountDownLatch

そもそも Latch (ラッチ) とは?

 * 以下の関連記事を参照のこと
https://blogs.yahoo.co.jp/dk521123/37076571.html

CountDownLatchについて

 * スレッドを待機させることができる
 * 特定の条件が満たされるまですべてのスレッドを保留し、
  その条件が満たされると、すべてのスレッドを同時に解放

使いどころ

 * 非同期で待ち合わせる処理

 例えば、以下に書いてあるような「2つのサービスに接続済みである必要があり、サービスへの接続を待機する場合」
http://yuki312.blogspot.jp/2012/03/androidcountdownlatch.html

■ 主なメソッド

https://docs.oracle.com/javase/jp/6/api/java/util/concurrent/CountDownLatch.html

await()

 => スレッドで割り込みが発生しないかぎり、ラッチのカウントダウンがゼロになるまで
    現在のスレッドを待機させる。

 * 2種類ある

 [1] public void await()

 [2] public boolean await(long timeout, TimeUnit unit)

     パラメータ:
       + timeout - 待機する最長時間
       + unit - timeout 引数の時間単位
     戻り値:
       + true  : カウントがゼロに達した場合
       + false : カウントがゼロに達する前に待機時間が経過した場合

countDown()

 => ラッチのカウントを減算し、カウントがゼロに達すると待機中のスレッドをすべて解放する
【構文】(finally句に書く方がいい)
SampleMethod(CountDownLatch countDownLatch) {
  try {
    // 処理
  } catch (Exception ex) {
    ex.printStackTrace();
  } finally {
    countDownLatch.countDown();
  }

getCount()

 => 現在のカウントを返す

■ サンプル

Main.java

import java.util.Calendar;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;

public class Main {
   private static final Class<?>[] SERVICE_CLASSES = new Class<?>[] {
         SampleServiceA.class, SampleServiceB.class };

   public static void main(String[] args) throws InterruptedException, ExecutionException {
      System.out.println(Calendar.getInstance().getTime().toString() + " START");
      
      CountDownLatch countDownLatch = new CountDownLatch(SERVICE_CLASSES.length);

      System.out.println(Calendar.getInstance().getTime().toString() + " Create SampleServiceA");
      SampleServiceA sampleServiceA = new SampleServiceA(countDownLatch);
      
      System.out.println(Calendar.getInstance().getTime().toString() + " Create SampleServiceB");
      SampleServiceB sampleServiceB = new SampleServiceB(countDownLatch);

      System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceA.run()");
      sampleServiceA.start();
      
      System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceB.run()");
      sampleServiceB.start();
      
      System.out.println(Calendar.getInstance().getTime().toString() + " countDownLatch.await() call");
      countDownLatch.await();

      System.out.println(Calendar.getInstance().getTime().toString() + " END");
   }
}

SampleServiceA.java

import java.util.Calendar;
import java.util.concurrent.CountDownLatch;

public class SampleServiceA extends Thread {
   private CountDownLatch countDownLatch;

   SampleServiceA(CountDownLatch countDownLatch) {
      this.countDownLatch = countDownLatch;
   }
   @Override
   public void run() {
      System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceA.call() START");
      long sleepTime = (long)(5000L * (Math.random() * 9 + 1));
      
      try {
         System.out.println("SampleServiceA.sleepTime = " + sleepTime);
         System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceA.Sleeping ... ");
         Thread.sleep(sleepTime);
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         System.out.println(Calendar.getInstance().getTime().toString() + " Before calling countDown " + this.countDownLatch.getCount());
         this.countDownLatch.countDown();
         System.out.println(Calendar.getInstance().getTime().toString() + " After calling countDown " + this.countDownLatch.getCount());
      }
      System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceA.call() END");
   }
}

SampleServiceB.java

import java.util.Calendar;
import java.util.concurrent.CountDownLatch;

public class SampleServiceB extends Thread {
   private CountDownLatch countDownLatch;

   SampleServiceB(CountDownLatch countDownLatch) {
      this.countDownLatch = countDownLatch;
   }
   @Override
   public void run() {
      System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceB.call() START");
      long sleepTime = (long)(5000L * (Math.random() * 9 + 1));
      
      try {
         System.out.println("SampleServiceB.sleepTime = " + sleepTime);
         System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceB.Sleeping ... ");
         Thread.sleep(sleepTime);
      } catch (Exception ex) {
         ex.printStackTrace();
      } finally {
         System.out.println(Calendar.getInstance().getTime().toString() + " Before calling countDown " + this.countDownLatch.getCount());
         this.countDownLatch.countDown();
         System.out.println(Calendar.getInstance().getTime().toString() + " After calling countDown " + this.countDownLatch.getCount());
      }
      System.out.println(Calendar.getInstance().getTime().toString() + " SampleServiceB.call() END");
   }
}
出力結果
Thu Nov 20 17:24:53 JST 2014 START
Thu Nov 20 17:24:54 JST 2014 Create SampleServiceA
Thu Nov 20 17:24:54 JST 2014 Create SampleServiceB
Thu Nov 20 17:24:54 JST 2014 SampleServiceA.run()
Thu Nov 20 17:24:54 JST 2014 SampleServiceB.run()
Thu Nov 20 17:24:54 JST 2014 SampleServiceA.call() START
Thu Nov 20 17:24:54 JST 2014 countDownLatch.await() call
SampleServiceA.sleepTime = 5364
Thu Nov 20 17:24:54 JST 2014 SampleServiceA.Sleeping ... 
Thu Nov 20 17:24:54 JST 2014 SampleServiceB.call() START
SampleServiceB.sleepTime = 46454
Thu Nov 20 17:24:54 JST 2014 SampleServiceB.Sleeping ... 
Thu Nov 20 17:24:59 JST 2014 Before calling countDown 2
Thu Nov 20 17:24:59 JST 2014 After calling countDown 1
Thu Nov 20 17:24:59 JST 2014 SampleServiceA.call() END
Thu Nov 20 17:25:40 JST 2014 Before calling countDown 1
Thu Nov 20 17:25:40 JST 2014 END // ★注目★ countDown()でカウント0になって即Mainスレッドに戻る
()
Thu Nov 20 17:25:40 JST 2014 After calling countDown 0
Thu Nov 20 17:25:40 JST 2014 SampleServiceB.call() END


関連記事

java.util.concurrent

java.util.concurrent について
http://blogs.yahoo.co.jp/dk521123/32538961.html
CountDownLatch ~カウントダウンラッチ~
http://blogs.yahoo.co.jp/dk521123/33538428.html
ExecutorService / Runnableインタフェース
http://blogs.yahoo.co.jp/dk521123/33665815.html
ScheduledExecutorService ~スケジューラ~
http://blogs.yahoo.co.jp/dk521123/33934102.html
ExecutorService / Callable / Future インタフェース
http://blogs.yahoo.co.jp/dk521123/34213161.html

タイマー

''' TaskTimer ===
http://blogs.yahoo.co.jp/dk521123/33926465.html

その他

コールバック関数を自作で実装する
 * コールバック関数で自作可能。
http://blogs.yahoo.co.jp/dk521123/34242678.html
【非同期】非同期・スレッドに関する用語
https://blogs.yahoo.co.jp/dk521123/37076571.html