Java : CountDownLatch (同期) - API使用例
CountDownLatch (Java SE 18 & JDK 18) の使用例まとめです。
 だいたいのメソッドを網羅済みです。
 API仕様のおともにどうぞ。
概要
ほかのスレッドで実行中の操作セットが完了するまで、1つ以上のスレッドを待機可能にする同期化支援機能です。
CountDownLatch は、スレッドの処理をブロック/リセットするためのクラスです。
大まかな流れは…
- 指定したカウントを持つ CountDownLatch を生成
 - メインとなるスレッドでは、await メソッドでカウントが 0 になるまで待機
 - 別のスレッドから countDown メソッドで、カウントを 1 減らす
 - カウントが 0 になったら、await で待機していたスレッドが復帰
 
となります。
コード例です。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newFixedThreadPool(2);
try {
    final var latch = new CountDownLatch(2);
    System.out.println("Main Thread : latch count = " + latch.getCount());
    executorService.submit(() -> {
        try {
            System.out.printf("  Thread A : start : %f sec.%n", elapsedSec.getAsDouble());
            TimeUnit.SECONDS.sleep(1);
            System.out.printf("  Thread A : end   : %f sec.%n", elapsedSec.getAsDouble());
        } finally {
            latch.countDown();
            System.out.println("  Thread A : latch count = " + latch.getCount());
        }
        return null;
    });
    executorService.submit(() -> {
        try {
            System.out.printf("  Thread B : start : %f sec.%n", elapsedSec.getAsDouble());
            TimeUnit.SECONDS.sleep(2);
            System.out.printf("  Thread B : end   : %f sec.%n", elapsedSec.getAsDouble());
        } finally {
            latch.countDown();
            System.out.println("  Thread B : latch count = " + latch.getCount());
        }
        return null;
    });
    System.out.printf("Main Thread : await start : %f sec.%n", elapsedSec.getAsDouble());
    latch.await();
    System.out.printf("Main Thread : await end : %f sec.%n", elapsedSec.getAsDouble());
} finally {
    executorService.shutdown();
}
final var term = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("term : " + term);
// 結果
// ↓
//Main Thread : latch count = 2
//  Thread A : start : 0.004276 sec.
//  Thread B : start : 0.004439 sec.
//Main Thread : await start : 0.004305 sec.
//  Thread A : end   : 1.006230 sec.
//  Thread A : latch count = 1
//  Thread B : end   : 2.007936 sec.
//  Thread B : latch count = 0
//Main Thread : await end : 2.008358 sec.
//term : true
 
      コーディングするときの注意
- countDown が呼び出されないと、await がずっと復帰されないことになります。
例えば、例外が発生して countDown の呼び出しがスキップされてしまった、ということが起きないように注意しましょう。 
関連記事 :
コンストラクタ
CountDownLatch (int count)
指定されたカウントで初期化されたCountDownLatchを構築します。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newSingleThreadExecutor();
try {
    final var latch = new CountDownLatch(1);
    executorService.submit(() -> {
        try {
            System.out.printf("  sleep start : %f sec.%n", elapsedSec.getAsDouble());
            TimeUnit.SECONDS.sleep(1);
            System.out.printf("  sleep end   : %f sec.%n", elapsedSec.getAsDouble());
        } finally {
            latch.countDown();
        }
        return null;
    });
    System.out.printf("await start : %f sec.%n", elapsedSec.getAsDouble());
    latch.await();
    System.out.printf("await end   : %f sec.%n", elapsedSec.getAsDouble());
} finally {
    executorService.shutdown();
}
final var term = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("term : " + term);
// 結果
// ↓
//await start : 0.001962 sec.
//  sleep start : 0.002234 sec.
//  sleep end   : 1.015228 sec.
//await end   : 1.015626 sec.
//term : true
 
      メソッド
void await ()
スレッドで割り込みが発生しないかぎり、ラッチのカウント・ダウンがゼロになるまで現在のスレッドを待機させます。
通常の使用例は、概要 および コンストラクタ のコード例をご参照ください。
 以下は、スレッド割り込みを発生させた例です。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newFixedThreadPool(2);
try {
    final var latch = new CountDownLatch(1);
    final var future = executorService.submit(() -> {
        try {
            System.out.printf("Thread A : start : %f sec.%n", elapsedSec.getAsDouble());
            latch.await();
        } catch (InterruptedException e) {
            System.out.println("Thread A : InterruptedException!");
        } finally {
            System.out.printf("Thread A : end : %f sec.%n", elapsedSec.getAsDouble());
        }
    });
    executorService.submit(() -> {
        System.out.printf("Thread B : sleep start : %f sec.%n", elapsedSec.getAsDouble());
        TimeUnit.SECONDS.sleep(5);
        System.out.printf("Thread B : sleep end   : %f sec.%n", elapsedSec.getAsDouble());
        // 割り込みを発生させます。
        final var ret = future.cancel(true);
        System.out.println("Thread B : cancel : " + ret);
        return null;
    });
} finally {
    executorService.shutdown();
}
final var term = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("term : " + term);
// 結果
// ↓
//Thread A : start : 0.002028 sec.
//Thread B : sleep start : 0.002209 sec.
//Thread B : sleep end   : 5.012301 sec.
//Thread A : InterruptedException!
//Thread A : end : 5.013477 sec.
//Thread B : cancel : true
//term : true
 
      boolean await (long timeout, TimeUnit unit)
スレッドで割り込みが発生するか、指定された待機時間が経過しないかぎり、ラッチのカウント・ダウンがゼロになるまで現在のスレッドを待機させます。
スレッドで割り込みが発生するケースは await() と同等となります。
 API使用例はそちらもご参照ください。
カウントがゼロになる例です。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newSingleThreadExecutor();
try {
    final var latch = new CountDownLatch(1);
    executorService.submit(() -> {
        try {
            System.out.printf("Thread A : sleep start : %f sec.%n", elapsedSec.getAsDouble());
            TimeUnit.SECONDS.sleep(2);
            System.out.printf("Thread A : sleep end   : %f sec.%n", elapsedSec.getAsDouble());
        } finally {
            latch.countDown();
        }
        return null;
    });
    final var ret = latch.await(5, TimeUnit.SECONDS);
    System.out.printf("await ret = %b : %f sec.%n", ret, elapsedSec.getAsDouble());
} finally {
    executorService.shutdown();
}
final var term = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("term : " + term);
// 結果
// ↓
//Thread A : sleep start : 0.002863 sec.
//Thread A : sleep end   : 2.005293 sec.
//await ret = true : 2.005695 sec.
//term : true
 
       タイムアウトが発生する例です。
// 基準となる時刻
final long current = System.nanoTime();
// 基準となる時刻からの差分を秒として取得
final DoubleSupplier elapsedSec = () -> (System.nanoTime() - current) / 1000000000.0;
final var executorService = Executors.newSingleThreadExecutor();
try {
    final var latch = new CountDownLatch(1);
    executorService.submit(() -> {
        try {
            System.out.printf("Thread A : sleep start : %f sec.%n", elapsedSec.getAsDouble());
            TimeUnit.SECONDS.sleep(7);
            System.out.printf("Thread A : sleep end   : %f sec.%n", elapsedSec.getAsDouble());
        } finally {
            latch.countDown();
        }
        return null;
    });
    final var ret = latch.await(5, TimeUnit.SECONDS);
    System.out.printf("await ret = %b : %f sec.%n", ret, elapsedSec.getAsDouble());
} finally {
    executorService.shutdown();
}
final var term = executorService.awaitTermination(10, TimeUnit.SECONDS);
System.out.println("term : " + term);
// 結果
// ↓
//Thread A : sleep start : 0.002323 sec.
//await ret = false : 5.009722 sec.
//Thread A : sleep end   : 7.009743 sec.
//term : true
 
      void countDown ()
ラッチのカウントを減算し、カウントがゼロに達すると待機中のスレッドをすべて解放します。
long getCount ()
現在のカウントを返します。
getCount の使用例は、概要 のコード例をご参照ください。
String toString ()
ラッチおよびその状態を識別する文字列を返します。
final var latch = new CountDownLatch(2);
final var str1 = latch.toString();
System.out.println(str1); // java.util.concurrent.CountDownLatch@2de56eb2[Count = 2]
latch.countDown();
final var str2 = latch.toString();
System.out.println(str2); // java.util.concurrent.CountDownLatch@2de56eb2[Count = 1]
latch.countDown();
final var str3 = latch.toString();
System.out.println(str3); // java.util.concurrent.CountDownLatch@2de56eb2[Count = 0]
 
      関連記事
- API 使用例 
        
- BlockingQueue (ブロッキング・キュー)
 - BrokenBarrierException
 - Callable
 - CancellationException
 - ConcurrentHashMap.KeySetView (並列処理用セット)
 - ConcurrentLinkedDeque (並列処理用・両端キュー)
 - ConcurrentLinkedQueue (並列処理用キュー)
 - ConcurrentMap (並列処理用マップ)
 - ConcurrentModificationException (並列処理例外)
 - ConcurrentSkipListSet (並列処理用セット)
 - Condition (同期)
 - CopyOnWriteArrayList (並列処理用リスト)
 - CopyOnWriteArraySet (並列処理用セット)
 - CyclicBarrier (同期)
 - Exchanger (同期)
 - ExecutionException
 - Executor
 - ExecutorService
 - Executors
 - Future
 - Future.State
 - FutureTask
 - InterruptedException (割込み例外)
 - Lock (同期)
 - Object (オブジェクト)
 - Runnable
 - Semaphore (セマフォ)
 - Thread (スレッド)
 - ThreadGroup
 - ThreadLocal
 - TimeoutException
 - TimeUnit
 
 



