モジュール java.base
パッケージ java.util.concurrent

クラスPhaser


  • public class Phaser
    extends Object
    再使用可能な同期化バリアーで、機能はCyclicBarrierおよびCountDownLatchと同様ですが、より柔軟な使用方法をサポートします。

    登録 他のバリアーの場合とは異なり、フェーザ上で同期をとるために登録されるパーティの数は、時間とともに変化することがあります。 タスクは(メソッドregister()bulkRegister(int)、またはパーティの初期の数を確立するコンストラクタの形式を使用して)いつでも登録でき、任意の到着時に(arriveAndDeregister()を使用して)必要に応じて登録を解除できます。 もっとも基本的な同期構造と同様に、登録と登録解除は内部のカウントにしか影響を与えません。それ以上の内部登録は確立されないため、タスクは、自身が登録されているかどうかを照会できません。 (ただし、このクラスのサブクラス化によってこのような登録を導入できます。)

    同期 CyclicBarrierと同様に、 Phaserは繰返し待機できます。 arriveAndAwaitAdvance()メソッドには、CyclicBarrier.awaitに類似した効果があります。 フェーザの各世代には、関連付けられたフェーズ番号があります。 このフェーズ番号は0から始まり、すべてのパーティがフェーザに到着すると進められ、 Integer.MAX_VALUEに達すると0にラップされます。 フェーズ番号を使用すると、登録されたいずれかのパーティから呼び出すことのできる2種類のメソッドを通して、フェーザへの到着時や、ほかを待機しているときのアクションを個別に制御できます。

    • 受信数 arrive()メソッドとarriveAndDeregister()メソッドは、到着を記録します。 これらのメソッドはブロックを行わず、関連付けられた到着フェーズ番号、つまり、その到着が適用されるフェーザのフェーズ番号を返します。 特定のフェーズの最後のパーティが到着すると、オプションのアクションが実行され、フェーズが進められます。 これらのアクションは、フェーズをトリガーして進めるパーティによって実行され、終了も制御するオーバーライド・メソッドonAdvance(int, int)によって調整されます。 このメソッドのオーバーライドは CyclicBarrierへのバリアー・アクションの提供に似ていますが、それより柔軟です。
    • Waiting awaitAdvance(int)メソッドは、到着フェーズ番号を示す引数を必要とし、フェーザが別のフェーズに進むと(または、すでにそのフェーズにあるときは)復帰します。 CyclicBarrierを使用した類似の構築とは異なり、awaitAdvanceメソッドは、待機中のスレッドが割り込まれた場合でも引き続き待機します。 割込み可能なバージョンやタイムアウト・バージョンも使用できますが、タスクが割込み可能な状態またはタイム・アウトで待機中に検出された例外によって、フェーザの状態は変更されません。 必要に応じて、しばしばforceTerminationを呼び出したあとに、これらの例外のハンドラ内で関連する任意の回復を実行できます。 フェイザは、ForkJoinPoolで実行中のタスクでも使用できます。 pool parallelismLevelが同時にブロックされたパーティの最大数に対応できる場合は、進捗が保証されます。

    終了 フェーザは終了状態に入ることがあり、これはisTerminated()メソッドを使用してチェックできます。 終了時は、負の戻り値で示されるように、すべての同期メソッドが、フェーズが進むのを待機せずにただちに復帰します。 同様に、終了時に登録しようとしても効果はありません。 終了は、onAdvanceの呼出しがtrueを返したときにトリガーされます。 デフォルトの実装では、登録解除によって登録済みパーティの数が0になった場合に trueを返します。 下に示されているように、フェーザが固定の反復数を使用してアクションを制御しているときは、多くの場合、現在のフェーズ番号がしきい値に達したときに終了するようにこのメソッドをオーバーライドすると便利です。 また、forceTermination()メソッドを使用すると、待機中のスレッドを強制的に解放して終了可能にすることもできます。

    階層化。 フェーザは、競合を減らすために、階層型にする(つまり、ツリー構造で構築する)ことができます。 多数のパーティが含まれるために、通常であれば大きな同期競合コストが発生するフェーザを、代わりに、サブフェーザのグループが共通の親を共有するように設定できます。 これにより、操作ごとのオーバーヘッドが増えるとしても、スループットが大幅に向上する可能性があります。

    階層型フェーザのツリーでは、子フェーザのその親への登録と登録解除は自動的に管理されます。 子フェーザの登録済みパーティの数が(Phaser(Phaser,int)コンストラクタ、register()、またはbulkRegister(int)で確立された) 0以外になった場合は常に、子フェーザがその親に登録されます。 arriveAndDeregister()の呼出しの結果として登録済みパーティの数が0になった場合は常に、子フェーザがその親から登録解除されます。

    監視 同期メソッドを呼び出すことができるのは登録済みパーティだけですが、フェーザの現在の状態はどの呼出し側でも監視できます。 どの時点でも、合計でgetRegisteredParties()パーティが存在し、そのうちのgetArrivedParties()が現在のフェーズ(getPhase())に到着しています。 残りの(getUnarrivedParties())パーティが到着すると、フェーズが進められます。 これらのメソッドによって返された値は一時的な状態を反映している可能性があるため、一般に、同期の制御には役立ちません。 toString()メソッドは、これらの状態クエリーのスナップショットを非公式の監視に便利な形式で返します。

    使用例:

    Phaserは、可変数のパーティを処理する単発的なアクションを制御するために、CountDownLatchの代わりに使用できます。 典型的なイディオムは、これを最初の登録まで設定するメソッドのためのもので、次にすべてのアクションを開始し、次に登録解除します:

     
     void runTasks(List<Runnable> tasks) {
       Phaser startingGate = new Phaser(1); // "1" to register self
       // create and start threads
       for (Runnable task : tasks) {
         startingGate.register();
         new Thread(() -> {
           startingGate.arriveAndAwaitAdvance();
           task.run();
         }).start();
       }
    
       // deregister self to allow threads to proceed
       startingGate.arriveAndDeregister();
     }

    一連のスレッドで、指定された反復数だけアクションを繰返し実行させるための1つの方法として、onAdvanceをオーバーライドします。

     
     void startTasks(List<Runnable> tasks, int iterations) {
       Phaser phaser = new Phaser() {
         protected boolean onAdvance(int phase, int registeredParties) {
           return phase >= iterations - 1 || registeredParties == 0;
         }
       };
       phaser.register();
       for (Runnable task : tasks) {
         phaser.register();
         new Thread(() -> {
           do {
             task.run();
             phaser.arriveAndAwaitAdvance();
           } while (!phaser.isTerminated());
         }).start();
       }
       // allow threads to proceed; don't wait for them
       phaser.arriveAndDeregister();
     }
    メイン・タスクがあとで終了を待機する必要がある場合は、再登録してから、同様のループを実行できます。
     
       // ...
       phaser.register();
       while (!phaser.isTerminated())
         phaser.arriveAndAwaitAdvance();

    関連する構築を使用すると、このフェーズが決してInteger.MAX_VALUEをラップしないことが確実なコンテキストで、特定のフェーズ番号を待機できます。 たとえば、

     
     void awaitPhase(Phaser phaser, int phase) {
       int p = phaser.register(); // assumes caller not already registered
       while (p < phase) {
         if (phaser.isTerminated())
           // ... deal with unexpected termination
         else
           p = phaser.arriveAndAwaitAdvance();
       }
       phaser.arriveAndDeregister();
     }

    フェーザのツリーを使用してnタスクのセットを作成するには、構築時に登録するPhaserを受け入れるコンストラクタを備えたTaskクラスを想定すると、次の形式のコードを使用できます。 build(new Task[n], 0, n, new Phaser())の呼出しのあと、たとえばプールに送信することによって、これらのタスクを起動できます。

     
     void build(Task[] tasks, int lo, int hi, Phaser ph) {
       if (hi - lo > TASKS_PER_PHASER) {
         for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
           int j = Math.min(i + TASKS_PER_PHASER, hi);
           build(tasks, i, j, new Phaser(ph));
         }
       } else {
         for (int i = lo; i < hi; ++i)
           tasks[i] = new Task(ph);
           // assumes new Task(ph) performs ph.register()
       }
     }
    TASKS_PER_PHASERの最適な値は主に、想定される同期の頻度によって異なります。 フェーズごとのタスク本体が極端に小さい(そのため、頻度が高い)場合は4程度の小さい値が、また極端に大きい場合は最大数百の値が適している可能性があります。

    実装上の注意: この実装では、パーティの最大数を65535に制限します。 追加のパーティを登録しようとすると、IllegalStateExceptionが発生します。 ただし、任意の大きな参加者のセットを格納するために階層型フェーザを作成できるため、その方法を取るようにしてください。

    導入されたバージョン:
    1.7
    • コンストラクタのサマリー

      コンストラクタ 
      コンストラクタ 説明
      Phaser()
      最初に登録されたパーティを含まず、親を持たない、初期フェーズ番号0の新規フェーザを作成します。
      Phaser​(int parties)
      指定された数の登録済み未到着パーティを含み、親を持たず、初期フェーズ番号0の新規フェーザを作成します。
      Phaser​(Phaser parent)
      Phaser(parent, 0)と同等です。
      Phaser​(Phaser parent, int parties)
      指定された親を持ち、指定された数の登録済み未到着パーティを含む新規フェーザを作成します。
    • メソッドのサマリー

      すべてのメソッド インスタンス・メソッド 具象メソッド 
      修飾子と型 メソッド 説明
      int arrive()
      他の到着を待機せずにこのフェーザに到着します。
      int arriveAndAwaitAdvance()
      このフェーザに到着し、他を待機します。
      int arriveAndDeregister()
      このフェーザに到着し、他の到着を待機せずに登録解除します。
      int awaitAdvance​(int phase)
      このフェーザのフェーズが指定されたフェーズ値から進むまで待機し、現在のフェーズが指定されたフェーズ値と等しくないか、このフェーザが終了した場合はすぐに復帰します。
      int awaitAdvanceInterruptibly​(int phase)
      このフェーザのフェーズが指定されたフェーズ値から進むまで待機し、待機中に割込みが発生した場合はInterruptedExceptionをスローし、現在のフェーズが指定されたフェーズ値と等しくないか、このフェーザが終了した場合はすぐに復帰します。
      int awaitAdvanceInterruptibly​(int phase, long timeout, TimeUnit unit)
      このフェーザのフェーズが指定されたフェーズ値から進むか、指定されたタイム・アウト時間が経過するまで待機し、待機中に割込みが発生した場合は InterruptedExceptionをスローし、現在のフェーズが指定されたフェーズ値と等しくないか、このフェーザが終了した場合はすぐに復帰します。
      int bulkRegister​(int parties)
      指定された数の新しい未到着パーティをこのフェーザに追加します。
      void forceTermination()
      このフェーザを強制的に終了状態にします。
      int getArrivedParties()
      このフェーザの現在のフェーズに到着した登録済みパーティの数を返します。
      Phaser getParent()
      このフェーザの親を返します。存在しない場合はnullです。
      int getPhase()
      現在のフェーズ番号を返します。
      int getRegisteredParties()
      このフェーザに登録されたパーティの数を返します。
      Phaser getRoot()
      このフェーザの上位ノードとなるルートを返します。フェーザが親を持たない場合はこのフェーザと同じです。
      int getUnarrivedParties()
      このフェーザの現在のフェーズにまだ到着していない登録済みパーティの数を返します。
      boolean isTerminated()
      このフェーザが終了した場合はtrueを返します。
      protected boolean onAdvance​(int phase, int registeredParties)
      フェーズを進める直前にアクションを実行し、終了を制御するオーバーライド可能なメソッドです。
      int register()
      未到着の新規パーティをこのフェーザに追加します。
      String toString()
      このフェーザおよびその状態を識別する文字列を返します。
    • コンストラクタの詳細

      • Phaser

        public Phaser()
        最初に登録されたパーティを含まず、親を持たない、初期フェーズ番号0の新規フェーザを作成します。 このフェーザを使用するスレッドはすべて、最初にこれに登録する必要があります。
      • Phaser

        public Phaser​(int parties)
        指定された数の登録済み未到着パーティを含み、親を持たず、初期フェーズ番号0の新規フェーザを作成します。
        パラメータ:
        parties - 次のフェーズに進むために必要なパーティの数
        例外:
        IllegalArgumentException - パーティが0未満か、サポートされるパーティの最大数より大きい場合
      • Phaser

        public Phaser​(Phaser parent)
        Phaser(parent, 0)と同等です。
        パラメータ:
        parent - 親フェーザ
      • Phaser

        public Phaser​(Phaser parent,
                      int parties)
        指定された親を持ち、指定された数の登録済み未到着パーティを含む新規フェーザを作成します。 指定された親がnull以外であり、指定されたパーティの数が0より大きい場合、この子フェーザはその親に登録されます。
        パラメータ:
        parent - 親フェーザ
        parties - 次のフェーズに進むために必要なパーティの数
        例外:
        IllegalArgumentException - パーティが0未満か、サポートされるパーティの最大数より大きい場合
    • メソッドの詳細

      • register

        public int register()
        未到着の新規パーティをこのフェーザに追加します。 onAdvance(int, int)の呼出しが進行中の場合、このメソッドは、復帰する前に実行を待機することがあります。 このフェーザが親を持ち、かつ以前にこのフェーザに登録済みパーティがなかった場合は、この子フェーザもその親に登録されます。 このフェーザが終了する場合、登録しようとしても効果はなく、負の値が返されます。
        戻り値:
        この登録が適用される到着フェーズ番号です。 この値が負の場合、このフェーザは終了し、このとき登録には効果がありません。
        例外:
        IllegalStateException - サポートされるパーティの最大数よりも多く登録しようとした場合
      • bulkRegister

        public int bulkRegister​(int parties)
        指定された数の新しい未到着パーティをこのフェーザに追加します。 onAdvance(int, int)の呼出しが進行中の場合、このメソッドは、復帰する前に実行を待機することがあります。 このフェーザが親を持ち、指定されたパーティの数が0より大きく、かつ以前にこのフェーザに登録済みパーティがなかった場合は、この子フェーザもその親に登録されます。 このフェーザが終了する場合、登録しようとしても効果はなく、負の値が返されます。
        パラメータ:
        parties - 次のフェーズに進むために必要な追加のパーティの数
        戻り値:
        この登録が適用される到着フェーズ番号です。 この値が負の場合、このフェーザは終了し、このとき登録には効果がありません。
        例外:
        IllegalStateException - サポートされるパーティの最大数よりも多く登録しようとした場合
        IllegalArgumentException - parties < 0の場合
      • arrive

        public int arrive()
        他の到着を待機せずにこのフェーザに到着します。

        未登録のパーティがこのメソッドを呼び出すと、使用方法のエラーになります。 ただし、このエラーが発生したとしても、その結果はこのフェーザの一部の後続動作に限定され IllegalStateExceptionとなります。

        戻り値:
        到着フェーズ番号。終了している場合は負の値
        例外:
        IllegalStateException - 終了しておらず、未到着パーティの数が負の場合
      • arriveAndDeregister

        public int arriveAndDeregister()
        このフェーザに到着し、他の到着を待機せずに登録解除します。 登録解除によって、将来のフェーズに進むために必要なパーティの数が減ります。 このフェーザが親を持ち、かつ登録解除によってこのフェーザのパーティの数が0になる場合は、このフェーザもその親から登録解除されます。

        未登録のパーティがこのメソッドを呼び出すと、使用方法のエラーになります。 ただし、このエラーが発生したとしても、その結果はこのフェーザの一部の後続動作に限定され IllegalStateExceptionとなります。

        戻り値:
        到着フェーズ番号。終了している場合は負の値
        例外:
        IllegalStateException - 終了しておらず、登録済みパーティまたは未到着パーティの数が負になる場合
      • arriveAndAwaitAdvance

        public int arriveAndAwaitAdvance()
        このフェーザに到着し、他を待機します。 実質的にawaitAdvance(arrive())と同等です。 割り込みまたはタイム・アウトで待機する必要がある場合は、その他のいずれかの形式の awaitAdvanceメソッドを使用して、類似の構築でこれを調整できます。 代わりに、到着時に登録解除する必要がある場合は、awaitAdvance(arriveAndDeregister())を使用します。

        未登録のパーティがこのメソッドを呼び出すと、使用方法のエラーになります。 ただし、このエラーが発生したとしても、その結果はこのフェーザの一部の後続動作に限定され IllegalStateExceptionとなります。

        戻り値:
        到着フェーズ番号。終了している場合は(負の) 現在のフェーズ
        例外:
        IllegalStateException - 終了しておらず、未到着パーティの数が負の場合
      • awaitAdvance

        public int awaitAdvance​(int phase)
        このフェーザのフェーズが指定されたフェーズ値から進むまで待機し、現在のフェーズが指定されたフェーズ値と等しくないか、このフェーザが終了した場合はすぐに復帰します。
        パラメータ:
        phase - 到着フェーズ番号。終了している場合は負の値。この引数は通常、arriveまたはarriveAndDeregisterを以前呼び出したときに返される値です。
        戻り値:
        次の到着フェーズ番号。それが負の場合は引数。あるいは終了の場合は(負の) 現在のフェーズ
      • awaitAdvanceInterruptibly

        public int awaitAdvanceInterruptibly​(int phase)
                                      throws InterruptedException
        このフェーザのフェーズが指定されたフェーズ値から進むまで待機し、待機中に割込みが発生した場合はInterruptedExceptionをスローし、現在のフェーズが指定されたフェーズ値と等しくないか、このフェーザが終了した場合はすぐに復帰します。
        パラメータ:
        phase - 到着フェーズ番号。終了している場合は負の値。この引数は通常、arriveまたはarriveAndDeregisterを以前呼び出したときに返される値です。
        戻り値:
        次の到着フェーズ番号。それが負の場合は引数。あるいは終了の場合は(負の) 現在のフェーズ
        例外:
        InterruptedException - 待機中にスレッドの割込みが発生した場合
      • awaitAdvanceInterruptibly

        public int awaitAdvanceInterruptibly​(int phase,
                                             long timeout,
                                             TimeUnit unit)
                                      throws InterruptedException,
                                             TimeoutException
        このフェーザのフェーズが指定されたフェーズ値から進むか、指定されたタイム・アウト時間が経過するまで待機し、待機中に割込みが発生した場合は InterruptedExceptionをスローし、現在のフェーズが指定されたフェーズ値と等しくないか、このフェーザが終了した場合はすぐに復帰します。
        パラメータ:
        phase - 到着フェーズ番号。終了している場合は負の値。この引数は通常、arriveまたはarriveAndDeregisterを以前呼び出したときに返される値です。
        timeout - 処理を中止するまでの待機時間。単位はunit
        unit - timeoutパラメータの解釈方法を決定するTimeUnit
        戻り値:
        次の到着フェーズ番号。それが負の場合は引数。あるいは終了の場合は(負の) 現在のフェーズ
        例外:
        InterruptedException - 待機中にスレッドの割込みが発生した場合
        TimeoutException - 待機中にタイム・アウトした場合
      • forceTermination

        public void forceTermination()
        このフェーザを強制的に終了状態にします。 登録済みパーティの数は影響を受けません。 このフェーザがフェーザの階層型セットのメンバーである場合は、そのセット内のすべてのフェーザが終了します。 このフェーザがすでに終了している場合は、このメソッドに効果はありません。 このメソッドは、1つ以上のタスクで予期しない例外が発生したあとの回復の調整に役立つことがあります。
      • getPhase

        public final int getPhase()
        現在のフェーズ番号を返します。 最大のフェーズ番号はInteger.MAX_VALUEです。その後、0から再開します。 終了時、フェーズ番号は負です。この場合は、終了の前の現在のフェーズをgetPhase()+Integer.MIN_VALUEで取得できます。
        戻り値:
        フェーズ番号。終了している場合は負の値
      • getRegisteredParties

        public int getRegisteredParties()
        このフェーザに登録されたパーティの数を返します。
        戻り値:
        パーティの数
      • getArrivedParties

        public int getArrivedParties()
        このフェーザの現在のフェーズに到着した登録済みパーティの数を返します。 このフェーザが終了した場合、返された値は意味のない任意の値です。
        戻り値:
        到着済みパーティの数
      • getUnarrivedParties

        public int getUnarrivedParties()
        このフェーザの現在のフェーズにまだ到着していない登録済みパーティの数を返します。 このフェーザが終了した場合、返された値は意味のない任意の値です。
        戻り値:
        未到着パーティの数
      • getParent

        public Phaser getParent()
        このフェーザの親を返します。存在しない場合はnullです。
        戻り値:
        このフェーザの親。ない場合はnull
      • getRoot

        public Phaser getRoot()
        このフェーザの上位ノードとなるルートを返します。フェーザが親を持たない場合はこのフェーザと同じです。
        戻り値:
        このフェーザの上位ノードとなるルート
      • isTerminated

        public boolean isTerminated()
        このフェーザが終了した場合はtrueを返します。
        戻り値:
        このフェーザが終了している場合はtrue
      • onAdvance

        protected boolean onAdvance​(int phase,
                                    int registeredParties)
        フェーズを進める直前にアクションを実行し、終了を制御するオーバーライド可能なメソッドです。 このメソッドは、このフェーザを進めるパーティの到着時に呼び出されます(その他の待機中のパーティがすべて休止している場合)。 このメソッドが trueを返す場合、このフェーザは前進時に最終終了状態に設定され、その後のisTerminated()の呼び出しはtrueを返します。 このメソッドの呼出しによってスローされた(非チェック)例外またはエラーはすべて、このフェーザを進めようとしているパーティに伝播されます。この場合、フェーザは進められません。

        このメソッドへの引数は、現在の移行を行っているフェーザの状態を提供します。 onAdvance内からこのフェーザに対して到着、登録、および待機メソッドを呼び出したときの効果は指定されていないため、それに依存してはいけません。

        このフェーザがフェーザの階層型セットのメンバーである場合、onAdvanceは、フェーズを進めるたびにそのルート・フェーザに対してのみ呼び出されます。

        もっとも一般的なユース・ケースをサポートするために、このメソッドのデフォルトの実装は、パーティのarriveAndDeregisterの呼出しの結果として登録済みパーティの数が0になったときにtrueを返します。 常にfalseを返すようにこのメソッドをオーバーライドすることによって、この動作を無効にし、それにより将来の登録時の継続を有効にすることができます。

         
         Phaser phaser = new Phaser() {
           protected boolean onAdvance(int phase, int parties) { return false; }
         }
        パラメータ:
        phase - このメソッドに入ったときの、このフェーザが進められる前の現在のフェーズ番号
        registeredParties - 登録済みパーティの現在の数
        戻り値:
        このフェーザを終了する必要がある場合はtrue
      • toString

        public String toString()
        このフェーザおよびその状態を識別する文字列を返します。 括弧内の状態は、文字列 "phase = "と、その後に続くフェーズ番号"parties = "と、登録されたパーティ数と、 "arrived = "と、その後に到着したパーティ数を含む。
        オーバーライド:
        toString 、クラス:  Object
        戻り値:
        このフェーザおよびその状態を識別する文字列