Skip to content
閲覧中:
6. Fork/Join フレームワーク (71-85)

6. Fork/Join フレームワーク (71-85)

Fork/Join フレームワークは、大きなタスクを小さなサブタスクに分割(Fork)し、それぞれの結果を統合(Join)して最終的な答えを出す「分割統治法」を並列で行うための仕組みです。Java Gold試験では、**クラスの継承関係とメソッドの呼び出し順序**が頻出します。

71番から85番までの項目をすべて網羅して解説します。


71-73. Fork/Join の主要クラス

タスクの種類によって継承するクラスを使い分けます。

Java
// 71. ForkJoinPool: タスクを実行する専用のプール
ForkJoinPool pool = new ForkJoinPool();

// 72. RecursiveTask<V>: 戻り値があるタスク (V型を返す)
class SumTask extends RecursiveTask<Long> { ... }

// 73. RecursiveAction: 戻り値がないタスク (void)
class PrintTask extends RecursiveAction { ... }

74-77. 基本的なメソッドと処理の流れ

試験で最も問われる「再帰処理」のテンプレートコードです。

Java
class MyTask extends RecursiveTask<Integer> {
    private final int n;
    MyTask(int n) { this.n = n; }

    // 74. compute(): タスクのメインロジックを記述するメソッド
    @Override
    protected Integer compute() {
        if (n <= 10) { // 79. 閾値(これ以上分割しない基準)
            return n * 2;
        }

        // 75. fork(): サブタスクを別スレッドで非同期に実行開始
        MyTask subTask = new MyTask(n - 10);
        subTask.fork(); 

        // 76. join(): サブタスクの完了を待ち、結果を受け取る
        // 77. invoke(): 最初の一歩としてプールにタスクを投げる際にも使われる
        return n * 2 + subTask.join();
    }
}

78-80. 実行の仕組みとプール

内部的な動きとパフォーマンスに関する項目です。

  • 78. ワークスティーリング (Work-Stealing): 自分のタスクが終わって暇になったスレッドが、他の忙しいスレッドのタスク行列の「後ろ」からタスクを盗んで実行する仕組み。これにより効率を最大化します。
  • 79. 閾値 (Threshold): タスクをこれ以上分割するとオーバーヘッドが大きすぎるという「止め時」の設定。
  • 80. commonPool(): 明示的に new ForkJoinPool() しなくても、ForkJoinPool.commonPool() で共有のプールを取得できます。

81-85. 注意点と応用

  • 81. 呼び出し順序の罠: 試験では sub1.fork(); sub2.fork(); sub2.join(); sub1.join(); という順序がよく出ます。これなら並列に走りますが、sub1.fork(); sub1.join(); と書くと逐次処理(シングルスレッド)と同じになってしまいます。
  • 82. 実行時例外: RecursiveTask 内で例外が起きると、join() を呼んでいる側で ExecutionException などがスローされます。
  • 83. invokeAll(): 複数のタスクを一度にフォークし、すべて終わるまで待機する便利なメソッド。
  • 84. 戻り値の型: RecursiveTask<V>V は、クラス宣言時の型パラメータと一致している必要があります。
  • 85. 並列数のデフォルト: デフォルトでは Runtime.getRuntime().availableProcessors() - 1 のスレッドが使われます。

💡 Java Gold 試験対策の「急所」

  1. 戻り値があるかないか?:
  2. 問題文のコードが RecursiveTask なのに compute() の戻り値が void だったり、逆に RecursiveAction なのに return していたら即座にコンパイルエラーと判断してください。

  3. fork()join() の位置:

  4. join() は「結果が必要になる直前」に呼びます。fork() してすぐ join() するのは並列化の失敗パターン(ひっかけ)です。

  5. どのメソッドをオーバーライドするか?:

  6. 必ず compute() です。run()call() ではありません。

これで並列処理の技術的な核心部分はほぼ網羅しました! 最後は、「86-100. 並列Streamの深掘り」 です。ここまでの「並列の仕組み」が Stream API とどう融合するかを仕上げます。準備はいいですか?