並行ユーティリティ
概要
Java は複数のスレッド間の同期操作を調整するための様々な並行ユーティリティクラスを提供している。
Semaphore セマフォ
Semaphore は複数のスレッドが同時に共有リソースにアクセスすることを制御する同期メカニズム。同時アクセスできるスレッド数を制限できる。

Semaphore は
acquire(取得)と release(解放)の二つの操作を提供する。スレッドが共有リソースにアクセスする際、まずセマフォを取得する必要がある。セマフォが利用可能であれば取得成功、そうでなければ他のスレッドがセマフォを解放するまでブロックされる。SemaphoreExample.java
Semaphore semaphore = new Semaphore(3); // 最大 3 スレッドが同時アクセス可能
// パーミット取得
semaphore.acquire();
try {
// 共有リソースにアクセス
} finally {
// パーミット解放
semaphore.release();
}CountDownLatch カウントダウンラッチ
CountDownLatch は一つまたは複数のスレッドが他のスレッドグループの操作完了を待つことができる同期ツール。

CountDownLatch はカウンターを含み、カウンターが零になると待機しているスレッドが解放される。スレッドが作業を完了するたびに、カウンターを一つ減らす。
CountDownLatchExample.java
CountDownLatch latch = new CountDownLatch(3);
// ワーカースレッド
for (int i = 0; i < 3; i++) {
new Thread(() -> {
// 作業実行
System.out.println(Thread.currentThread().getName() + " 作業完了");
latch.countDown(); // カウンター -1
}).start();
}
// メインスレッド待機
latch.await(); // カウンターが零になるまで待機
System.out.println("全作業完了");CyclicBarrier サイクリックバリア
CyclicBarrier は一グループのスレッドが特定のバリアポイントで互いを待ち、全てのスレッドが到達した後に実行を継続することを許可する。

CyclicBarrier は再利用可能で、全てのスレッドがバリアポイントに到達すると、次の使用のためにリセットされる。
CyclicBarrierExample.java
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("全スレッドがバリアポイントに到達");
});
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " バリアに到達");
try {
barrier.await(); // 他のスレッドを待機
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " 実行継続");
}).start();
}CountDownLatch vs CyclicBarrier
| 特性 | CountDownLatch | CyclicBarrier |
|---|---|---|
| 再利用 | 不可 | 可能 |
| カウント方式 | 零までカウントダウン | 全てのスレッド到達を待つ |
| 用途 | イベントグループの完了待ち | スレッド間の相互待機 |
Exchanger エクスチェンジャー
Exchanger は二つのスレッド間でデータを交換するための同期ポイント。各スレッドは Exchanger ポイントで待機し、他のスレッドも到達するとデータを交換して実行を継続する。

ExchangerExample.java
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
try {
String data = "データ A";
String received = exchanger.exchange(data);
System.out.println("スレッド 1 受信: " + received);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
String data = "データ B";
String received = exchanger.exchange(data);
System.out.println("スレッド 2 受信: " + received);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();Phaser フェイザー
Phaser は複数フェーズの実行を同期制御するための、より柔軟な同期メカニズムを提供する。

各フェーズには任意の数のスレッドを含めることができ、全てのスレッドがそのフェーズを完了した時のみ次のフェーズに進める。Phaser は CyclicBarrier より柔軟で、参加者を動的に登録・解除できる。
PhaserExample.java
Phaser phaser = new Phaser(3); // 3 参加者
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " フェーズ 1 完了");
phaser.arriveAndAwaitAdvance(); // 他のスレッドがフェーズ 1 を完了するのを待機
System.out.println(Thread.currentThread().getName() + " フェーズ 2 完了");
phaser.arriveAndAwaitAdvance(); // 他のスレッドがフェーズ 2 を完了するのを待機
System.out.println(Thread.currentThread().getName() + " 全フェーズ完了");
phaser.arriveAndDeregister(); // 完了して登録解除
}).start();
}まとめ比較
| ユーティリティ | 用途 | 再利用 | 動的参加者 |
|---|---|---|---|
| Semaphore | リソースへの同時アクセススレッド数を制限 | 可 | - |
| CountDownLatch | イベントグループの完了待ち | 不可 | 不可 |
| CyclicBarrier | スレッド間の相互待機 | 可 | 不可 |
| Exchanger | 二スレッド間のデータ交換 | 可 | - |
| Phaser | 複数フェーズ同期 | 可 | 可 |