【Java学習|初心者向け】Javaの並行処理をマスターしよう!CyclicBarrierで複数のタスクを同期させる方法

1. 導入:なぜCyclicBarrierが必要なのか?

並行処理において、複数のスレッドで重い処理を並列実行し、すべての処理が終わったタイミングで「次のステップ」へ進みたいというケースはよくあります。例えば、ゲームのロード画面で「プレイヤー情報」「マップデータ」「テクスチャ」を個別に読み込み、すべて完了した後にゲームを開始するような状況です。これを実現するための強力なツールが、java.util.concurrent.CyclicBarrierです。

2. 基礎知識:CyclicBarrierとは?

CyclicBarrierは、指定した数のスレッドが特定の地点(バリアポイント)に到達するまで、それらのスレッドを待機させるための同期用クラスです。「Cyclic(循環的)」という名の通り、バリアが解除された後に再度利用できるのが特徴です。

主な用語:
パーティ数(parties):待機させるスレッドの数。
バリアアクション(barrier action):すべてのスレッドが到達した瞬間に一度だけ実行されるタスク。

3. 実装/解決策

CyclicBarrierを利用するには、まず「待機させたいスレッド数」を指定してインスタンスを生成します。各スレッド内でawait()メソッドを呼び出すことで、指定数に達するまで処理がブロックされます。

4. サンプルプログラム

以下のコードは、3つのスレッドが処理を完了した時点で、メイン処理が実行される例です。

import java.util.concurrent.CyclicBarrier;

public class BarrierExample {
    public static void main(String[] args) {
        int numberOfThreads = 3;

        // 全スレッドが到達した後に実行されるアクション
        Runnable barrierAction = () -> System.out.println("★全員集合!次のステージへ進みます。");

        // 3つのスレッドを待機させるバリアを生成
        CyclicBarrier barrier = new CyclicBarrier(numberOfThreads, barrierAction);

        for (int i = 1; i <= numberOfThreads; i++) {
            new Thread(new Worker(barrier, i)).start();
        }
    }
}

class Worker implements Runnable {
    private CyclicBarrier barrier;
    private int id;

    public Worker(CyclicBarrier barrier, int id) {
        this.barrier = barrier;
        this.id = id;
    }

    public void run() {
        try {
            System.out.println("スレッド " + id + " が処理を開始します...");
            Thread.sleep((long) (Math.random()  3000)); // 処理時間をシミュレート
            System.out.println("スレッド " + id + " がバリア地点に到達しました。");
            
            // ここで待機。指定数に達するまで処理が止まる
            barrier.await(); 
            
            System.out.println("スレッド " + id + " が再開しました!");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5. 応用・注意点:現場で陥りやすい罠

現場で活用する際には、以下の点に注意してください。

例外処理:await()メソッドは、InterruptedExceptionやBrokenBarrierExceptionをスローします。バリアが壊れた場合(他のスレッドが中断された場合など)、すべてが停止してしまうため、適切なエラーハンドリングが必要です。
タイムアウトの検討:await(long timeout, TimeUnit unit)を使うと、指定時間内に全員が揃わない場合に例外を投げることができます。無限に待機し続けるデッドロックを防ぐため、実務ではタイムアウト付きの利用を推奨します。
最新技術との併用:Java 21以降のVirtual Threads環境でもCyclicBarrierは動作しますが、複雑な非同期処理を行う場合は、より柔軟なCompletableFutureのallOf()や、構造化並行処理(Structured Concurrency)の利用も検討しましょう。

CyclicBarrierは、複数の処理の「足並みを揃える」ための非常に直感的な手段です。まずはこのサンプルを動かして、スレッドの動きを体感してみてください。

コメント

タイトルとURLをコピーしました