Java : CountDownLatch (同期) - API使用例
CountDownLatch (Java SE 18 & JDK 18) の使用例まとめです。
だいたいのメソッドを網羅済みです。
API仕様のおともにどうぞ。
概要
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)
// 基準となる時刻
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
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 (ブロッキング・キュー)
- Callable
- CancellationException
- ConcurrentHashMap.KeySetView (並列処理用セット)
- ConcurrentLinkedDeque (並列処理用・両端キュー)
- ConcurrentLinkedQueue (並列処理用キュー)
- ConcurrentMap (並列処理用マップ)
- ConcurrentModificationException (並列処理例外)
- ConcurrentSkipListSet (並列処理用セット)
- Condition (同期)
- CopyOnWriteArrayList (並列処理用リスト)
- CopyOnWriteArraySet (並列処理用セット)
- CyclicBarrier (同期)
- Exchanger (同期)
- Executor
- ExecutorService
- Executors
- Future
- Future.State
- FutureTask
- InterruptedException (割込み例外)
- Lock (同期)
- Object (オブジェクト)
- Runnable
- Semaphore (セマフォ)
- Thread (スレッド)
- ThreadGroup
- ThreadLocal
- TimeUnit