並行ユーティリティ

概要

Java は複数ふくすうのスレッドかん同期どうき操作そうさ調整ちょうせいするための様々さまざま並行へいこうユーティリティクラスを提供ていきょうしている。

Semaphore セマフォ

Semaphore は複数ふくすうのスレッドが同時どうじ共有きょうゆうリソースにアクセスすることを制御せいぎょする同期どうきメカニズム。同時どうじアクセスできるスレッドすう制限せいげんできる。

Semaphore

Semaphore は acquire取得しゅとく)と release解放かいほう)のふたつの操作そうさ提供ていきょうする。スレッドが共有きょうゆうリソースにアクセスするさい、まずセマフォを取得しゅとくする必要ひつようがある。セマフォが利用りよう可能かのうであれば取得しゅとく成功せいこう、そうでなければほかのスレッドがセマフォを解放かいほうするまでブロックされる。
SemaphoreExample.java
Semaphore semaphore = new Semaphore(3); // 最大 3 スレッドが同時アクセス可能

// パーミット取得
semaphore.acquire();
try {
    // 共有リソースにアクセス
} finally {
    // パーミット解放
    semaphore.release();
}

CountDownLatch カウントダウンラッチ

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

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

特性とくせいCountDownLatchCyclicBarrier
再利用さいりよう不可ふか可能かのう
カウント方式ほうしきゼロまでカウントダウンすべてのスレッド到達とうたつ
用途ようとイベントグループの完了かんりょうスレッドかん相互そうご待機たいき

Exchanger エクスチェンジャー

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

かくフェーズには任意にんいかずのスレッドをふくめることができ、すべてのスレッドがそのフェーズを完了かんりょうしたときのみつぎのフェーズにすすめる。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複数ふくすうフェーズ同期どうき

参考リソース