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