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
で実行中のタスクでも使用できます。 プールの並列度レベルが同時にブロックされるパーティの最大数に対応できる場合は、進捗が確認されます。
終了 フェーザは終了状態に入ることがあり、これは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
-
コンストラクタのサマリー
-
メソッドのサマリー
修飾子と型 メソッド 説明 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 = "
と、その後に到着したパーティ数を含む。
-