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
- 処理を中止するまでの待機時間。単位はunit
unit
- timeout
パラメータの解釈方法を決定するTimeUnit
InterruptedException
- 待機中にスレッドの割込みが発生した場合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
です。null
public Phaser getRoot()
public boolean isTerminated()
true
を返します。true
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
バグまたは機能を送信
詳細なAPIリファレンスおよび開発者ドキュメントについては、Java SEのドキュメントを参照してください。そのドキュメントには、概念的な概要、用語の定義、回避方法、有効なコード例などの、開発者を対象にしたより詳細な説明が含まれています。
Copyright© 1993, 2014, Oracle and/or its affiliates. All rights reserved.