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へのバリアー・アクションの提供に似ていますが、それより柔軟です。
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) {
final Phaser phaser = new Phaser(1); // "1" to register self
// create and start threads
for (final Runnable task : tasks) {
phaser.register();
new Thread() {
public void run() {
phaser.arriveAndAwaitAdvance(); // await all creation
task.run();
}
}.start();
}
// allow threads to start and deregister self
phaser.arriveAndDeregister();
}
一連のスレッドで、指定された反復数だけアクションを繰返し実行させるための1つの方法として、onAdvanceをオーバーライドします。
void startTasks(List<Runnable> tasks, final int iterations) {
final Phaser phaser = new Phaser() {
protected boolean onAdvance(int phase, int registeredParties) {
return phase >= iterations || registeredParties == 0;
}
};
phaser.register();
for (final Runnable task : tasks) {
phaser.register();
new Thread() {
public void run() {
do {
task.run();
phaser.arriveAndAwaitAdvance();
} while (!phaser.isTerminated());
}
}.start();
}
phaser.arriveAndDeregister(); // deregister self, don't wait
}
メイン・タスクがあとで終了を待機する必要がある場合は、再登録してから、同様のループを実行できます。
// ...
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が発生します。 ただし、任意の大きな参加者のセットを格納するために階層型フェーザを作成できるため、その方法を取るようにしてください。
| コンストラクタ | 説明 |
|---|---|
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() |
このフェーザおよびその状態を識別する文字列を返します。
|
public Phaser()
public Phaser(int parties)
parties - 次のフェーズに進むために必要なパーティの数IllegalArgumentException - パーティが0未満か、サポートされるパーティの最大数より大きい場合public Phaser(Phaser parent)
Phaser(parent, 0)と同等です。parent - 親フェーザpublic Phaser(Phaser parent, int parties)
parent - 親フェーザparties - 次のフェーズに進むために必要なパーティの数IllegalArgumentException - パーティが0未満か、サポートされるパーティの最大数より大きい場合public int register()
onAdvance(int, int)の呼出しが進行中の場合、このメソッドは、復帰する前に実行を待機することがあります。 このフェーザが親を持ち、かつ以前にこのフェーザに登録済みパーティがなかった場合は、この子フェーザもその親に登録されます。 このフェーザが終了する場合、登録しようとしても効果はなく、負の値が返されます。 IllegalStateException - サポートされるパーティの最大数よりも多く登録しようとした場合public int bulkRegister(int parties)
onAdvance(int, int)の呼出しが進行中の場合、このメソッドは、復帰する前に実行を待機することがあります。 このフェーザが親を持ち、指定されたパーティの数が0より大きく、かつ以前にこのフェーザに登録済みパーティがなかった場合は、この子フェーザもその親に登録されます。 このフェーザが終了する場合、登録しようとしても効果はなく、負の値が返されます。 parties - 次のフェーズに進むために必要な追加のパーティの数IllegalStateException - サポートされるパーティの最大数よりも多く登録しようとした場合IllegalArgumentException - parties < 0の場合public int arrive()
未登録のパーティがこのメソッドを呼び出すと、使用方法のエラーになります。 ただし、このエラーが発生したとしても、その結果はこのフェーザの一部の後続動作に限定されIllegalStateExceptionとなります。
IllegalStateException - 終了しておらず、未到着パーティの数が負の場合public int arriveAndDeregister()
未登録のパーティがこのメソッドを呼び出すと、使用方法のエラーになります。 ただし、このエラーが発生したとしても、その結果はこのフェーザの一部の後続動作に限定されIllegalStateExceptionとなります。
IllegalStateException - 終了しておらず、登録済みパーティまたは未到着パーティの数が負になる場合public int arriveAndAwaitAdvance()
awaitAdvance(arrive())と同等です。 割り込みまたはタイム・アウトで待機する必要がある場合は、その他のいずれかの形式のawaitAdvanceメソッドを使用して、類似の構築でこれを調整できます。 代わりに、到着時に登録解除する必要がある場合は、awaitAdvance(arriveAndDeregister())を使用します。
未登録のパーティがこのメソッドを呼び出すと、使用方法のエラーになります。 ただし、このエラーが発生したとしても、その結果はこのフェーザの一部の後続動作に限定されIllegalStateExceptionとなります。
IllegalStateException - 終了しておらず、未到着パーティの数が負の場合public int awaitAdvance(int phase)
phase - 到着フェーズ番号。終了している場合は負の値。この引数は通常、arriveまたはarriveAndDeregisterを以前呼び出したときに返される値です。public int awaitAdvanceInterruptibly(int phase)
throws InterruptedException
InterruptedExceptionをスローし、現在のフェーズが指定されたフェーズ値と等しくないか、このフェーザが終了した場合はすぐに復帰します。phase - 到着フェーズ番号。終了している場合は負の値。この引数は通常、arriveまたはarriveAndDeregisterを以前呼び出したときに返される値です。InterruptedException - 待機中にスレッドの割込みが発生した場合public int awaitAdvanceInterruptibly(int phase,
long timeout,
TimeUnit unit)
throws InterruptedException,
TimeoutException
InterruptedExceptionをスローし、現在のフェーズが指定されたフェーズ値と等しくないか、このフェーザが終了した場合はすぐに復帰します。phase - 到着フェーズ番号。終了している場合は負の値。この引数は通常、arriveまたはarriveAndDeregisterを以前呼び出したときに返される値です。timeout - 処理を中止するまでの待機時間。単位はunitunit - timeoutパラメータの解釈方法を決定するTimeUnitInterruptedException - 待機中にスレッドの割込みが発生した場合TimeoutException - 待機中にタイム・アウトした場合public void forceTermination()
public final int getPhase()
Integer.MAX_VALUEです。その後、0から再開します。 終了時、フェーズ番号は負です。この場合は、終了の前の現在のフェーズをgetPhase()+Integer.MIN_VALUEで取得できます。 public int getRegisteredParties()
public int getArrivedParties()
public int getUnarrivedParties()
public Phaser getParent()
nullです。nullpublic Phaser getRoot()
public boolean isTerminated()
trueを返します。trueprotected 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 バグまたは機能を送信
詳細なAPIリファレンスおよび開発者ドキュメントについては、Java SEのドキュメントを参照してください。 そのドキュメントには、概念的な概要、用語の定義、回避方法、有効なコード例などの、開発者を対象にしたより詳細な説明が含まれています。
Copyright © 1993, 2025, Oracle and/or its affiliates. All rights reserved. Use is subject to license terms. Documentation Redistribution Policyも参照してください。