public class Semaphore extends Object implements Serializable
acquire()
は、パーミットが利用可能になるまで必要に応じてブロックし、その後パーミットを取得します。各release()
はパーミットを追加し、場合によってはブロックしている取得側を解放します。ただし、実際のパーミット・オブジェクトは使用されません。Semaphore
は単に利用可能な個数のカウントを保持し、それに応じた処理を行います。
セマフォは、物理的または論理的な一部のリソースにアクセス可能なスレッド数を制限するためによく使用されます。たとえば、次のクラスでは、セマフォを使用して項目のプールへのアクセスを制御します。
class Pool {
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
public Object getItem() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void putItem(Object x) {
if (markAsUnused(x))
available.release();
}
// Not a particularly efficient data structure; just for demo
protected Object[] items = ... whatever kinds of items being managed
protected boolean[] used = new boolean[MAX_AVAILABLE];
protected synchronized Object getNextAvailableItem() {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null; // not reached
}
protected synchronized boolean markAsUnused(Object item) {
for (int i = 0; i < MAX_AVAILABLE; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
} else
return false;
}
}
return false;
}
}
項目を取得する前に、各スレッドはセマフォから、項目が利用可能であることを保証するパーミットを取得する必要があります。項目の処理が完了するとスレッドはプールに戻り、パーミットがセマフォに返され、ほかのスレッドがその項目を取得できるようになります。acquire()
が呼び出されたとき、同期ロックは保持されないことに注意してください。同期ロックが保持されると、項目がプールに返されなくなるためです。セマフォは、プールへのアクセスを制限する必要のある同期を、プール自体の一貫性を維持するために必要な同期からは分離してカプセル化します。
値を1に初期化されたセマフォは、利用できるパーミットが最大で1個であるセマフォとして使用されるため、相互排他ロックとして利用できます。これは一般的には2進型セマフォと呼ばれます。利用可能なパーミットが1つか、または利用可能なパーミットがないかの2つの状態しかないためです。このように使用された場合、多くのLock
実装とは異なり、2進型セマフォには、所有者以外のスレッドでロックを解放できるという特性があります(セマフォには所有権の概念がないため)。これは、デッドロックの回復のような特殊なコンテキストで便利です。
このクラスのコンストラクタは、オプションで公平性パラメータを受け入れます。falseに設定すると、このクラスはスレッドがパーミットを取得する順序について保証しません。特に、バージ(barging)が許可されています。つまり、acquire()
を呼び出すスレッドに、待機していたスレッドより先にパーミットを割り当てることができます。論理的には、新しいスレッドが、待機中のスレッドのキューの先頭に配置されます。公平性がtrueに設定されると、セマフォは、acquire
メソッドのいずれかを呼び出すスレッドが、これらのメソッドの呼出しが処理された順序(先入れ先出し、FIFO)でパーミットを取得するように選択されることを保証します。FIFO順序付けは、必然的にこれらのメソッド内の特定の内部実行ポイントに適用されます。そのため、あるスレッドが別のスレッドより前にacquire
を呼び出しても、そのスレッドよりあとに順序付けポイントに到達する可能性があります。また、メソッドからの復帰時も同様です。また、時間指定のないtryAcquire
メソッドは公平性の設定に従いませんが、利用可能なパーミットをすべて取得することにも注意してください。
通常、リソース・アクセスを制御するために使用されるセマフォは、リソースへのアクセスができないスレッドがないよう、公平に初期化される必要があります。ほかの種類の同期制御にセマフォを使用する場合は、公平性を考慮するよりも不公平な順序付けによるスループットの利点のほうがしばしば重要になります。
このクラスはまた、複数のパーミットに対して同時にacquire
やrelease
を実行するための簡易メソッドも提供します。公平性をtrueに設定せずにこれらのメソッドを使用すると、無期限に延期される危険が増すことに注意してください。
メモリー整合性効果: release()
などの「解放」メソッドを呼び出す前のスレッド内のアクションは、別のスレッド内のacquire()
などの正常終了した「取得」メソッドに続くアクションの前に発生します。
コンストラクタと説明 |
---|
Semaphore(int permits)
指定された数のパーミットと不公平な公平性設定を使用して、
Semaphore を作成します。 |
Semaphore(int permits, boolean fair)
指定された数のパーミットと指定された公平性設定を使用して、
Semaphore を作成します。 |
修飾子と型 | メソッドと説明 |
---|---|
void |
acquire()
このセマフォからパーミットを取得します。パーミットが利用可能になるか、またはスレッドが割り込みされるまでブロックします。
|
void |
acquire(int permits)
このセマフォから指定された数のパーミットを取得します。すべてのパーミットが利用可能になるか、またはスレッドが割り込みされるまでブロックします。
|
void |
acquireUninterruptibly()
このセマフォからパーミットを取得します。パーミットが利用可能になるまでブロックします。
|
void |
acquireUninterruptibly(int permits)
このセマフォから指定された数のパーミットを取得します。すべてのパーミットが利用可能になるまでブロックします。
|
int |
availablePermits()
このセマフォで現在利用可能なパーミットの数を返します。
|
int |
drainPermits()
すぐに利用可能なすべてのパーミットを取得して返します。
|
protected Collection<Thread> |
getQueuedThreads()
パーミットの取得を待機しているスレッドを含むコレクションを返します。
|
int |
getQueueLength()
パーミットの取得を待機しているスレッドの推定数を返します。
|
boolean |
hasQueuedThreads()
パーミットの取得を待機中のスレッドが存在するかどうかを照会します。
|
boolean |
isFair()
このセマフォで公平性がtrueに設定されている場合は
true を返します。 |
protected void |
reducePermits(int reduction)
指定されたreductionの数だけ利用可能なパーミットの数を減らします。
|
void |
release()
パーミットを解放し、セマフォに戻します。
|
void |
release(int permits)
指定された数のパーミットを解放し、セマフォに戻します。
|
String |
toString()
セマフォおよびその状態を識別する文字列を返します。
|
boolean |
tryAcquire()
パーミットが呼出し時に利用可能な場合に限り、このセマフォからパーミットを取得します。
|
boolean |
tryAcquire(int permits)
指定された数のパーミットが呼出し時に利用可能な場合に限り、それらすべてのパーミットを取得します。
|
boolean |
tryAcquire(int permits, long timeout, TimeUnit unit)
指定された待機時間内で指定された数のパーミットが利用可能であり、現在のスレッドで割り込みが発生していない場合に、このセマフォから指定された数のパーミットを取得します。
|
boolean |
tryAcquire(long timeout, TimeUnit unit)
指定された待機時間内でパーミットが利用可能になり、現在のスレッドで割り込みが発生していない場合に、このセマフォからパーミットを取得します。
|
public Semaphore(int permits)
Semaphore
を作成します。permits
- 利用可能なパーミットの初期の数。この値は負にすることも可能で、その場合は取得メソッドが許可を受け取る前に解放が発生する必要がある。public Semaphore(int permits, boolean fair)
Semaphore
を作成します。permits
- 利用可能なパーミットの初期の数。この値は負にすることも可能で、その場合は取得メソッドが許可を受け取る前に解放が発生する必要がある。fair
- このセマフォが競合時にパーミットの許可を先入れ先出しで保証する場合はtrue
。それ以外の場合はfalse
public void acquire() throws InterruptedException
パーミットが利用可能な場合はパーミットを取得してすぐに復帰するため、利用可能なパーミットの数は1つずつ減ります。
パーミットが利用可能でない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、次の2つのいずれかが起きるまで待機します。
現在のスレッドで、
InterruptedException
がスローされ、現在のスレッドの割込みステータスがクリアされます。InterruptedException
- 現在のスレッドで割込みが発生した場合public void acquireUninterruptibly()
パーミットが利用可能な場合はパーミットを取得してすぐに復帰するため、利用可能なパーミットの数は1つずつ減ります。
パーミットが使用できない場合、現在のスレッドはスレッドのスケジューリングの目的に対して無効になり、ほかのいずれかのスレッドがこのセマフォのrelease()
メソッドを呼び出して、現在のスレッドが次にパーミットを割り当てられるスレッドになるまで、現在のスレッドは待機します。
パーミットの待機中に現在のスレッドが割り込みされると、待機は続行しますが、スレッドにパーミットが割り当てられるタイミングは、割込みが発生しなかった場合にパーミットを受け取るはずのタイミングとは異なることがあります。スレッドがこのメソッドから復帰すると、その割込み状態が設定されます。
public boolean tryAcquire()
パーミットが利用可能な場合はパーミットを取得して値true
ですぐに復帰するため、利用可能なパーミットの数は1つずつ減ります。
パーミットが利用可能でない場合、このメソッドは値false
ですぐに復帰します。
このセマフォが公平順序付けポリシーを使用するように設定されている場合でも、パーミットが利用可能であれば、ほかのスレッドが現在待機しているかどうかに関係なく、tryAcquire()
の呼出しですぐにパーミットを取得します。この「バージ」(barging)動作により公平性が失われるとは言え、これは特定の状況下で有用です。公平性設定を尊重する場合は、ほぼ等価なtryAcquire(0, TimeUnit.SECONDS)
を使用します(これも割込みを検出する)。
true
。それ以外の場合はfalse
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException
パーミットが利用可能な場合はパーミットを取得して値true
ですぐに復帰するため、利用可能なパーミットの数は1つずつ減ります。
パーミットが利用可能でない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、次の3つのいずれかが起きるまで待機します。
release()
メソッドを呼び出し、現在のスレッドが次にパーミットを割り当てられるスレッドになる。
パーミットが取得された場合は、値true
が返されます。
現在のスレッドで、
InterruptedException
がスローされ、現在のスレッドの割込みステータスがクリアされます。
指定された待機時間が経過すると、値false
が返されます。時間がゼロまたはそれより小さい場合、メソッドは待機しません。
timeout
- パーミットの最長待機時間unit
- timeout
引数の時間単位true
。パーミットが取得される前に待機時間が経過した場合はfalse
InterruptedException
- 現在のスレッドで割込みが発生した場合public void release()
パーミットを解放すると、利用可能なパーミットの数が1つずつ増えます。いくつかのスレッドがパーミットを取得しようと試みている場合は、その中の1つのスレッドが選択され、解放されたばかりのパーミットが与えられます。そのスレッドは、スレッドのスケジューリングに関して(ふたたび)有効になります。
パーミットを解放するスレッドは、acquire()
の呼出しでそのパーミットを取得している必要はありません。セマフォの適切な使用法は、アプリケーションでのプログラミング規約で確立されます。
public void acquire(int permits) throws InterruptedException
指定された数のパーミットが利用可能な場合はパーミットを取得してすぐに復帰するため、利用可能なパーミットの数は指定された数ずつ減ります。
十分な数のパーミットが利用可能でない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、次の2つのいずれかが起きるまで待機します。
release
メソッドのいずれかを呼び出し、現在のスレッドが次にパーミットを割り当てられるスレッドになり、利用可能なパーミットの数がこの要求を満たす。
現在のスレッドで、
InterruptedException
がスローされ、現在のスレッドの割込みステータスがクリアされます。このスレッドに割り当てられるはずだったすべてのパーミットは、release()
の呼出しでパーミットが利用可能になったかのように、パーミットの取得を試みる別のスレッドに割り当てられます。permits
- 取得するパーミットの数InterruptedException
- 現在のスレッドで割込みが発生した場合IllegalArgumentException
- permits
が負である場合public void acquireUninterruptibly(int permits)
指定された数のパーミットが利用可能な場合はパーミットを取得してすぐに復帰するため、利用可能なパーミットの数は指定された数ずつ減ります。
十分な数のパーミットが利用可能でない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、別のスレッドがこのセマフォに対してrelease
メソッドのいずれかを呼び出し、現在のスレッドが次にパーミットを割り当てられるスレッドになり、利用可能なパーミットの数がこの要求を満たすまで、現在のスレッドは待機します。
現在のスレッドがパーミットの待機中に割り込まれた場合、待機を続行します。キュー内での現在のスレッドの位置には影響ありません。スレッドがこのメソッドから復帰すると、その割込み状態が設定されます。
permits
- 取得するパーミットの数IllegalArgumentException
- permits
が負である場合public boolean tryAcquire(int permits)
指定された数のパーミットが利用可能な場合はパーミットを取得して値true
ですぐに復帰するため、利用可能なパーミットの数は指定された数ずつ減ります。
十分な数のパーミットが利用可能でない場合、このメソッドは値false
ですぐに復帰し、利用可能なパーミットの数は変化しません。
このセマフォが公平順序付けポリシーを使用するように設定されている場合でも、パーミットが利用可能であれば、ほかのスレッドが現在待機しているかどうかに関係なく、tryAcquire
の呼出しですぐにパーミットを取得します。この「バージ」(barging)動作により公平性が失われるとは言え、これは特定の状況下で有用です。公平性設定を尊重する場合は、ほぼ等価なtryAcquire(permits, 0, TimeUnit.SECONDS)
を使用します(これも割込みを検出する)。
permits
- 取得するパーミットの数true
。それ以外の場合はfalse
IllegalArgumentException
- permits
が負である場合public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException
指定された数のパーミットが利用可能な場合はパーミットを取得して値true
ですぐに復帰するため、利用可能なパーミットの数は指定された数ずつ減ります。
十分な数のパーミットが利用可能でない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、次の3つのいずれかが起きるまで待機します。
release
メソッドのいずれかを呼び出し、現在のスレッドが次にパーミットを割り当てられるスレッドになり、利用可能なパーミットの数がこの要求を満たす。
パーミットが取得された場合は、値true
が返されます。
現在のスレッドで、
InterruptedException
がスローされ、現在のスレッドの割込みステータスがクリアされます。このスレッドに割り当てられるはずだったすべてのパーミットは、release()
の呼出しでパーミットが利用可能になったかのように、パーミットの取得を試みる別のスレッドに割り当てられます。
指定された待機時間が経過すると、値false
が返されます。時間がゼロまたはそれより小さい場合、メソッドは待機しません。このスレッドに割り当てられるはずだったすべてのパーミットは、release()
の呼出しでパーミットが利用可能になったかのように、パーミットの取得を試みる別のスレッドに割り当てられます。
permits
- 取得するパーミットの数timeout
- パーミットの最長待機時間unit
- timeout
引数の時間単位true
。すべてのパーミットが取得される前に待機時間が経過した場合はfalse
InterruptedException
- 現在のスレッドで割込みが発生した場合IllegalArgumentException
- permits
が負である場合public void release(int permits)
指定された数のパーミットを解放すると、利用可能なパーミットの数がその分増えます。いくつかのスレッドがパーミットを取得しようと試みている場合、その中の1つのスレッドが選択されて、解放されたばかりのパーミットが与えられます。利用可能なパーミットの数がそのスレッドの要求を満たす場合、そのスレッドはスレッドのスケジューリングに関して(ふたたび)有効になります。そうでない場合、十分なパーミットが利用可能になるまでスレッドは待機します。このスレッドの要求を満たしたあとも引き続きパーミットが利用可能な場合、それらのパーミットはパーミットを取得しようとしている別のスレッドに割り当てられます。
パーミットを解放するスレッドは、acquire
の呼出しでそのパーミットを取得している必要はありません。セマフォの適切な使用法は、アプリケーションでのプログラミング規約で確立されます。
permits
- 解放するパーミットの数IllegalArgumentException
- permits
が負である場合public int availablePermits()
通常、このメソッドはデバッグとテストの場合に使用します。
public int drainPermits()
protected void reducePermits(int reduction)
acquire
と異なります。reduction
- 削除するパーミットの数IllegalArgumentException
- reduction
が負である場合public boolean isFair()
true
を返します。true
public final boolean hasQueuedThreads()
true
が返されても、ほかのいずれかのスレッドが取得するとは保証されていないことに注意してください。このメソッドは、主にシステム状態の監視に使用する目的で設計されています。true
public final int getQueueLength()
protected Collection<Thread> getQueuedThreads()
バグまたは機能を送信
詳細なAPIリファレンスおよび開発者ドキュメントについては、Java SEのドキュメントを参照してください。そのドキュメントには、概念的な概要、用語の定義、回避方法、有効なコード例などの、開発者を対象にしたより詳細な説明が含まれています。
Copyright © 1993, 2020, Oracle and/or its affiliates. All rights reserved. Use is subject to license terms. Documentation Redistribution Policyも参照してください。