JavaのCountDownLatchとCyclicBarrierの違いとは?マルチスレッド同期の使い分けを徹底解説
はじめに:2つの同期支援クラス
Javaでマルチスレッド環境を扱う際、複数のスレッドの動作を協調させるために活躍するのがCountDownLatchとCyclicBarrierです。どちらもjava.util.concurrentパッケージに含まれる同期支援クラスですが、設計思想や使い方、挙動が異なります。
本記事では、公式ドキュメント(JavaDoc)の定義をもとに、両者の違いを比較表とサンプルコードでわかりやすく解説します。
JavaDocによる定義
- CountDownLatch: 他のスレッドで実行されている一連の操作が完了するまで、1つ以上のスレッドを待機させるための同期支援クラス。
- CyclicBarrier: 複数のスレッドが互いに共通のバリアポイント(合流地点)へ到達するのを待ち合わせるための同期支援クラス。
主な違いの比較表
| No. | 比較項目 | CyclicBarrier | CountDownLatch |
|---|---|---|---|
| 1 | 基本 | 複数のスレッドが互いに共通のバリアポイントへ到達するのを待ち合わせる。 | 他のスレッドで実行中の一連の操作が完了するまで、1つ以上のスレッドを待機させる。 |
| 2 | Runnableの指定 | コンストラクタでRunnable(バリアアクション)を渡すことができる。 | Runnableを受け取るコンストラクタは存在しない。 |
| 3 | 管理対象 | 「スレッド」の数をカウントする。 | 「タスク」の数をカウントする。 |
| 4 | 前進可能性 | 参加スレッド自身がバリアポイントに到達することでのみ進行し、外部から進めることはできない。 | 任意のスレッドがcountDown()を呼び出すことでカウントを進められる。 |
| 5 | 例外の挙動 | 待機中のスレッドが割り込まれると、他の待機中スレッドにもBrokenBarrierExceptionが発生する。 | 現在のスレッドのみInterruptedExceptionが発生し、他のスレッドへの影響はない。 |
さらに重要な違いとして再利用性が挙げられます。CyclicBarrierは名前の通り「循環(Cyclic)」する仕組みを持っており、バリアポイントに全員が到達した後、自動的にリセットされて再利用できます。一方、CountDownLatchはカウントがゼロになった時点で役目を終え、再利用はできません。
CyclicBarrierの使用例
public class Main {
public static void main(String args[]) throws InterruptedException {
ExecutorService executors = Executors.newFixedThreadPool(4);
CyclicBarrier cyclicBarrier = new CyclicBarrier(5);
executors.submit(new Service1(cyclicBarrier));
executors.submit(new Service1(cyclicBarrier));
executors.submit(new Service2(cyclicBarrier));
executors.submit(new Service2(cyclicBarrier));
executors.submit(new Service2(cyclicBarrier));
Thread.sleep(3000);
System.out.println("Done");
}
}
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Service1 implements Runnable {
CyclicBarrier cyclicBarrier;
public Service1(CyclicBarrier cyclicBarrier) {
super();
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println("Services1" + cyclicBarrier.getNumberWaiting());
while (true) {
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class Service2 implements Runnable {
CyclicBarrier cyclicBarrier;
public Service2(CyclicBarrier cyclicBarrier) {
super();
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println("Services2" + cyclicBarrier.getNumberWaiting());
while (true) {
try {
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
CountDownLatchの使用例
public class Main {
public static void main(String args[]) throws InterruptedException {
ExecutorService executors = Executors.newFixedThreadPool(4);
CountDownLatch latch = new CountDownLatch(2);
executors.submit(new Service1(latch));
executors.submit(new Service2(latch));
latch.await();
System.out.println("Done");
}
}
import java.util.concurrent.CountDownLatch;
public class Service1 implements Runnable {
CountDownLatch latch;
public Service1(CountDownLatch latch) {
super();
this.latch = latch;
}
@Override
public void run() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
System.out.println("Service1" + latch.getCount());
}
}
import java.util.concurrent.CountDownLatch;
public class Service2 implements Runnable {
CountDownLatch latch;
public Service2(CountDownLatch latch) {
super();
this.latch = latch;
}
@Override
public void run() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
System.out.println("Service2" + latch.getCount());
}
}
まとめ
CountDownLatchは「開始の合図を待つ」「複数タスクの完了を待ち合わせる」といった一方向の待機処理に適した、使い切り型の同期クラスです。一方、CyclicBarrierは複数スレッドが互いの到達を待ち合わせるフェーズ同期に向いており、繰り返し再利用できる点が大きな特徴です。
用途に応じて、「一時的な完了待ちならCountDownLatch」「反復的な同期が必要ならCyclicBarrier」と使い分けることで、より安全で効率的な並行処理を実現できます。
-
JavaのIteratorとEnumerationの違いを徹底比較!使い分けのポイントを解説
はじめに:IteratorとEnumerationとはJavaのコレクションフレームワークには、コレクション内の要素を順番に走査(トラバース)しながらアクセスするための仕組みとして「カーソル」が用意されています。その代表格が Iterator(イテレータ) と Enumeration(列挙) の2つです。両者はどちらもコレクションフレームワークに属しますが、登場した時期と役割が異なります。EnumerationはJDK 1.0から存在する歴史あるインターフェースで、IteratorはJDK 1.2でコレクションフレームワークとともに導入された、より新しいインターフェースです。Enumerati
-
JavaでのArrayListとHashSetの違いを徹底解説!使い分けのポイントも紹介
HashSetとArrayListは、どちらもJavaコレクションフレームワークにおいて最も重要なクラスの一つです。それぞれ特徴が異なるため、用途に応じた適切な使い分けが求められます。本記事では、ArrayListとHashSetの主な違いを6つの観点から比較し、実際のサンプルコードとともにわかりやすく解説します。ArrayListとHashSetの違い一覧No.項目ArrayListHashSet1実装インターフェースListインターフェースを実装しています。Setインターフェースを実装しています。2内部構造内部的には配列(動的配列)を使って実装されています。内部的にはHashMapを使って