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, 2013, Oracle and/or its affiliates. All rights reserved.