モジュール java.base
パッケージ java.util.concurrent

クラスSemaphore

java.lang.Object
java.util.concurrent.Semaphore
すべての実装されたインタフェース:
Serializable

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メソッドは公平性の設定に従いませんが、利用可能なパーミットをすべて取得することにも注意してください。

通常、リソース・アクセスを制御するために使用されるセマフォは、リソースへのアクセスができないスレッドがないよう、公平に初期化される必要があります。 ほかの種類の同期制御にセマフォを使用する場合は、公平性を考慮するよりも不公平な順序付けによるスループットの利点のほうがしばしば重要になります。

このクラスはまた、複数のパーミットに対して同時にacquirereleaseを実行するための簡易メソッドも提供します。 これらのメソッドは一般に、ループより効率的かつ効果的です。 ただし、優先順位は設定されません。 たとえば、スレッドAがs.acquire(3を呼び出し、スレッドBがs.acquire(2)を呼び出し、2つの許可が利用可能になると、その獲得が最初に行われ、Semaphore sがフェア・モードでない限り、スレッドBがそれらを取得する保証はありません。

メモリー整合性効果: release()などの「解放」メソッドを呼び出す前のスレッド内のアクションは、別のスレッド内のacquire()などの正常終了した「取得」メソッドに続くアクションの前に発生します。

導入されたバージョン:
1.5
関連項目:
  • コンストラクタのサマリー

    コンストラクタ
    コンストラクタ
    説明
    Semaphore(int permits)
    指定された数のパーミットと不公平な公平性設定を使用して、Semaphoreを作成します。
    Semaphore(int permits, boolean fair)
    指定された数のパーミットと指定された公平性設定を使用して、Semaphoreを作成します。
  • メソッドのサマリー

    修飾子と型
    メソッド
    説明
    void
    このセマフォからパーミットを取得します。パーミットが利用可能になるか、またはスレッドが割り込みされるまでブロックします。
    void
    acquire(int permits)
    このセマフォから指定された数のパーミットを取得します。すべてのパーミットが利用可能になるか、またはスレッドが割り込みされるまでブロックします。
    void
    このセマフォからパーミットを取得します。パーミットが利用可能になるまでブロックします。
    void
    このセマフォから指定された数のパーミットを取得します。すべてのパーミットが利用可能になるまでブロックします。
    int
    このセマフォで現在利用可能なパーミットの数を返します。
    int
    すぐに利用可能なすべての許可を取得して返します。または、否定的な許可が利用可能な場合は、それらを解放します。
    protected Collection<Thread>
    パーミットの取得を待機しているスレッドを含むコレクションを返します。
    final int
    パーミットの取得を待機しているスレッドの推定数を返します。
    final boolean
    パーミットの取得を待機中のスレッドが存在するかどうかを照会します。
    boolean
    このセマフォで公平性がtrueに設定されている場合はtrueを返します。
    protected void
    reducePermits(int reduction)
    指定されたreductionの数だけ利用可能なパーミットの数を減らします。
    void
    パーミットを解放し、セマフォに戻します。
    void
    release(int permits)
    指定された数のパーミットを解放し、セマフォに戻します。
    セマフォおよびその状態を識別する文字列を返します。
    boolean
    パーミットが呼出し時に利用可能な場合に限り、このセマフォからパーミットを取得します。
    boolean
    tryAcquire(int permits)
    指定された数のパーミットが呼出し時に利用可能な場合に限り、それらすべてのパーミットを取得します。
    boolean
    tryAcquire(int permits, long timeout, TimeUnit unit)
    指定された待機時間内で指定された数のパーミットが利用可能であり、現在のスレッドで割り込みが発生していない場合に、このセマフォから指定された数のパーミットを取得します。
    boolean
    tryAcquire(long timeout, TimeUnit unit)
    指定された待機時間内でパーミットが利用可能になり、現在のスレッドで割り込みが発生していない場合に、このセマフォからパーミットを取得します。

    クラス java.lang.Objectで宣言されたメソッド

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait
  • コンストラクタの詳細

    • Semaphore

      public Semaphore(int permits)
      指定された数のパーミットと不公平な公平性設定を使用して、Semaphoreを作成します。
      パラメータ:
      permits - 利用可能なパーミットの初期の数。 この値は負にすることも可能で、その場合は取得メソッドが許可を受け取る前に解放が発生する必要がある。
    • Semaphore

      public Semaphore(int permits, boolean fair)
      指定された数のパーミットと指定された公平性設定を使用して、Semaphoreを作成します。
      パラメータ:
      permits - 利用可能なパーミットの初期の数。 この値は負にすることも可能で、その場合は取得メソッドが許可を受け取る前に解放が発生する必要がある。
      fair - このセマフォが競合時にパーミットの許可を先入れ先出しで保証する場合はtrue。それ以外の場合はfalse
  • メソッドの詳細

    • acquire

      public void acquire() throws InterruptedException
      このセマフォからパーミットを取得します。パーミットが利用可能になるか、またはスレッドが割り込みされるまでブロックします。

      パーミットが利用可能な場合はパーミットを取得してすぐに復帰するため、利用可能なパーミットの数は1つずつ減ります。

      パーミットが利用可能でない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、次の2つのいずれかが起きるまで待機します。

      • ほかのスレッドがこのセマフォに対してrelease()メソッドを呼び出し、現在のスレッドが次にパーミットを割り当てられるスレッドになる。
      • ほかのスレッドが現在のスレッドに割り込みを行う。

      現在のスレッドで、

      • このメソッドへのエントリ上で設定された割込みステータスが保持されるか、
      • パーミットの待機中に割り込みが発生した場合、
      InterruptedExceptionがスローされ、現在のスレッドの割込みステータスがクリアされます。

      例外:
      InterruptedException - 現在のスレッドで割込みが発生した場合
    • acquireUninterruptibly

      public void acquireUninterruptibly()
      このセマフォからパーミットを取得します。パーミットが利用可能になるまでブロックします。

      パーミットが利用可能な場合はパーミットを取得してすぐに復帰するため、利用可能なパーミットの数は1つずつ減ります。

      パーミットが使用できない場合、現在のスレッドはスレッドのスケジューリングの目的に対して無効になり、ほかのいずれかのスレッドがこのセマフォのrelease()メソッドを呼び出して、現在のスレッドが次にパーミットを割り当てられるスレッドになるまで、現在のスレッドは待機します。

      パーミットの待機中に現在のスレッドが割り込みされると、待機は続行しますが、スレッドにパーミットが割り当てられるタイミングは、割込みが発生しなかった場合にパーミットを受け取るはずのタイミングとは異なることがあります。 スレッドがこのメソッドから復帰すると、その割込み状態が設定されます。

    • tryAcquire

      public boolean tryAcquire()
      パーミットが呼出し時に利用可能な場合に限り、このセマフォからパーミットを取得します。

      パーミットが利用可能な場合はパーミットを取得して値trueですぐに復帰するため、利用可能なパーミットの数は1つずつ減ります。

      パーミットが利用可能でない場合、このメソッドは値falseですぐに復帰します。

      このセマフォが公平順序付けポリシーを使用するように設定されている場合でも、パーミットが利用可能であれば、ほかのスレッドが現在待機しているかどうかに関係なく、tryAcquire()の呼出しですぐにパーミットを取得します この「バージ」(barging)動作により公平性が失われるとは言え、これは特定の状況下で有用です。 公平性設定を尊重する場合は、ほぼ等価なtryAcquire(0, TimeUnit.SECONDS)を使用します(これも割込みを検出する)。

      戻り値:
      パーミットが取得された場合はtrue。それ以外の場合はfalse
    • tryAcquire

      public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException
      指定された待機時間内でパーミットが利用可能になり、現在のスレッドで割り込みが発生していない場合に、このセマフォからパーミットを取得します。

      パーミットが利用可能な場合はパーミットを取得して値trueですぐに復帰するため、利用可能なパーミットの数は1つずつ減ります。

      パーミットが利用可能でない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、次の3つのいずれかが起きるまで待機します。

      • ほかのスレッドがこのセマフォに対してrelease()メソッドを呼び出し、現在のスレッドが次にパーミットを割り当てられるスレッドになる。
      • ほかのスレッドが現在のスレッドに割り込みを行う。または
      • 指定された待機時間が経過する。

      パーミットが取得された場合は、値trueが返されます。

      現在のスレッドで、

      • このメソッドへのエントリ上で設定された割込みステータスが保持されるか、
      • パーミットの取得の待機中に割り込みが発生した場合、
      InterruptedExceptionがスローされ、現在のスレッドの割込みステータスがクリアされます。

      指定された待機時間が経過すると、値falseが返されます。 時間がゼロまたはそれより小さい場合、メソッドは待機しません。

      パラメータ:
      timeout - パーミットの最長待機時間
      unit - timeout引数の時間単位
      戻り値:
      パーミットが取得された場合はtrue。パーミットが取得される前に待機時間が経過した場合はfalse
      例外:
      InterruptedException - 現在のスレッドで割込みが発生した場合
    • release

      public void release()
      パーミットを解放し、セマフォに戻します。

      パーミットを解放すると、利用可能なパーミットの数が1つずつ増えます。 いくつかのスレッドがパーミットを取得しようと試みている場合は、その中の1つのスレッドが選択され、解放されたばかりのパーミットが与えられます。 そのスレッドは、スレッドのスケジューリングに関して(ふたたび)有効になります。

      パーミットを解放するスレッドは、acquire()の呼出しでそのパーミットを取得している必要はありません。 セマフォの適切な使用法は、アプリケーションでのプログラミング規約で確立されます。

    • acquire

      public void acquire(int permits) throws InterruptedException
      このセマフォから指定された数のパーミットを取得します。すべてのパーミットが利用可能になるか、またはスレッドが割り込みされるまでブロックします。

      指定された数のパーミットが利用可能な場合はパーミットを取得してすぐに復帰するため、利用可能なパーミットの数は指定された数ずつ減ります。 このメソッドは、ループをfor (int i = 0; i < permits; ++i) acquire();と同じ効果がありますが、パーツを原子的にまとめて取得する点が異なります:

      十分な数のパーミットが利用可能でない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、次の2つのいずれかが起きるまで待機します。

      • 他のスレッドはこのセマフォのreleaseメソッドの1つを呼び出し、現在のスレッドは次に許可を割り当てられ、使用可能な許可の数がこのリクエストを満たします。または
      • ほかのスレッドが現在のスレッドに割り込みを行う。

      現在のスレッドで、

      • このメソッドへのエントリ上で設定された割込みステータスが保持されるか、
      • パーミットの待機中に割り込みが発生した場合、
      InterruptedExceptionがスローされ、現在のスレッドの割込みステータスがクリアされます。 このスレッドに割り当てられるはずだったすべてのパーミットは、release()の呼出しでパーミットが利用可能になったかのように、パーミットの取得を試みる別のスレッドに割り当てられます。

      パラメータ:
      permits - 取得するパーミットの数
      例外:
      InterruptedException - 現在のスレッドで割込みが発生した場合
      IllegalArgumentException - permitsが負である場合
    • acquireUninterruptibly

      public void acquireUninterruptibly(int permits)
      このセマフォから指定された数のパーミットを取得します。すべてのパーミットが利用可能になるまでブロックします。

      指定された数のパーミットが利用可能な場合はパーミットを取得してすぐに復帰するため、利用可能なパーミットの数は指定された数ずつ減ります。 このメソッドは、ループをfor (int i = 0; i < permits; ++i) acquireUninterruptibly();と同じ効果がありますが、パーツを原子的にまとめて取得する点が異なります:

      不十分な許可が利用可能な場合、現在のスレッドはスレッド・スケジューリングの目的で無効になり、他のスレッドがこのセマフォのreleaseメソッドの1つを呼び出し、現在のスレッドが次に許可を割り当てられ、利用可能な許可の数がこのリクエストを満たすまで。

      現在のスレッドがパーミットの待機中に割り込まれた場合、待機を続行します。キュー内での現在のスレッドの位置には影響ありません。 スレッドがこのメソッドから復帰すると、その割込み状態が設定されます。

      パラメータ:
      permits - 取得するパーミットの数
      例外:
      IllegalArgumentException - permitsが負である場合
    • tryAcquire

      public boolean tryAcquire(int permits)
      指定された数のパーミットが呼出し時に利用可能な場合に限り、それらすべてのパーミットを取得します。

      指定された数のパーミットが利用可能な場合はパーミットを取得して値trueですぐに復帰するため、利用可能なパーミットの数は指定された数ずつ減ります。

      十分な数のパーミットが利用可能でない場合、このメソッドは値falseですぐに復帰し、利用可能なパーミットの数は変化しません。

      このセマフォが公平順序付けポリシーを使用するように設定されている場合でも、パーミットが利用可能であれば、ほかのスレッドが現在待機しているかどうかに関係なく、tryAcquireの呼出しですぐにパーミットを取得します この「バージ」(barging)動作により公平性が失われるとは言え、これは特定の状況下で有用です。 公平性設定を尊重する場合は、ほぼ等価なtryAcquire(permits, 0, TimeUnit.SECONDS)を使用します(これも割込みを検出する)。

      パラメータ:
      permits - 取得するパーミットの数
      戻り値:
      パーミットが取得された場合はtrue。それ以外の場合はfalse
      例外:
      IllegalArgumentException - permitsが負である場合
    • tryAcquire

      public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException
      指定された待機時間内で指定された数のパーミットが利用可能であり、現在のスレッドで割り込みが発生していない場合に、このセマフォから指定された数のパーミットを取得します。

      指定された数のパーミットが利用可能な場合はパーミットを取得して値trueですぐに復帰するため、利用可能なパーミットの数は指定された数ずつ減ります。

      十分な数のパーミットが利用可能でない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、次の3つのいずれかが起きるまで待機します。

      • 他のスレッドはこのセマフォのreleaseメソッドの1つを呼び出し、現在のスレッドは次に許可を割り当てられ、使用可能な許可の数がこのリクエストを満たします。または
      • ほかのスレッドが現在のスレッドに割り込みを行う。または
      • 指定された待機時間が経過する。

      パーミットが取得された場合は、値trueが返されます。

      現在のスレッドで、

      • このメソッドへのエントリ上で設定された割込みステータスが保持されるか、
      • パーミットの取得の待機中に割り込みが発生した場合、
      InterruptedExceptionがスローされ、現在のスレッドの割込みステータスがクリアされます。 このスレッドに割り当てられるはずだったすべてのパーミットは、release()の呼出しでパーミットが利用可能になったかのように、パーミットの取得を試みる別のスレッドに割り当てられます。

      指定された待機時間が経過すると、値falseが返されます。 時間がゼロまたはそれより小さい場合、メソッドは待機しません。 このスレッドに割り当てられるはずだったすべてのパーミットは、release()の呼出しでパーミットが利用可能になったかのように、パーミットの取得を試みる別のスレッドに割り当てられます。

      パラメータ:
      permits - 取得するパーミットの数
      timeout - パーミットの最長待機時間
      unit - timeout引数の時間単位
      戻り値:
      すべてのパーミットが取得された場合はtrue。すべてのパーミットが取得される前に待機時間が経過した場合はfalse
      例外:
      InterruptedException - 現在のスレッドで割込みが発生した場合
      IllegalArgumentException - permitsが負である場合
    • release

      public void release(int permits)
      指定された数のパーミットを解放し、セマフォに戻します。

      指定された数のパーミットを解放すると、利用可能なパーミットの数がその分増えます。 許可を取得しようとしているスレッドがあれば、1つのスレッドが選択され、今リリースされた許可が与えられます。 利用可能なパーミットの数がそのスレッドの要求を満たす場合、そのスレッドはスレッドのスケジューリングに関して(ふたたび)有効になります。そうでない場合、十分なパーミットが利用可能になるまでスレッドは待機します。 このスレッドの要求を満たしたあとも引き続きパーミットが利用可能な場合、それらのパーミットはパーミットを取得しようとしている別のスレッドに割り当てられます。

      パーミットを解放するスレッドは、acquireの呼出しでそのパーミットを取得している必要はありません。 セマフォの適切な使用法は、アプリケーションでのプログラミング規約で確立されます。

      パラメータ:
      permits - 解放するパーミットの数
      例外:
      IllegalArgumentException - permitsが負である場合
    • availablePermits

      public int availablePermits()
      このセマフォで現在利用可能なパーミットの数を返します。

      通常、このメソッドはデバッグとテストの場合に使用します。

      戻り値:
      このセマフォで利用可能なパーミットの数
    • drainPermits

      public int drainPermits()
      すぐに利用可能なすべての許可を取得して返します。または、否定的な許可が利用可能な場合は、それらを解放します。 返却時には、ゼロ許可が利用可能です。
      戻り値:
      取得された許可の数、または、もしあれば、リリースされた数
    • reducePermits

      protected void reducePermits(int reduction)
      指定されたreductionの数だけ利用可能なパーミットの数を減らします。 このメソッドは、利用不可能になるリソースを追跡するためにセマフォを使用するサブクラスで有用です。 パーミットが利用可能になるまでブロックして待機しない点で、このメソッドはacquireと異なります。
      パラメータ:
      reduction - 削除するパーミットの数
      例外:
      IllegalArgumentException - reductionが負である場合
    • isFair

      public boolean isFair()
      このセマフォで公平性がtrueに設定されている場合はtrueを返します。
      戻り値:
      このセマフォで公平性がtrueに設定されている場合はtrue
    • hasQueuedThreads

      public final boolean hasQueuedThreads()
      パーミットの取得を待機中のスレッドが存在するかどうかを照会します。 取消しはいつでも発生する可能性があるため、trueが返されても、ほかのいずれかのスレッドが取得するとは保証されていないことに注意してください。 このメソッドは、主にシステム状態の監視に使用する目的で設計されています。
      戻り値:
      ロックの取得を待機中のほかのスレッドが存在する可能性がある場合はtrue
    • getQueueLength

      public final int getQueueLength()
      パーミットの取得を待機しているスレッドの推定数を返します。 このメソッドが内部のデータ構造をトラバースしている間にも、スレッド数が動的に変化する場合があるため、この値は推定に過ぎません。 このメソッドは、同期の制御用としてではなく、システム状態の監視用として設計されています。
      戻り値:
      このロックを待機しているスレッドの推定数
    • getQueuedThreads

      protected Collection<Thread> getQueuedThreads()
      パーミットの取得を待機しているスレッドを含むコレクションを返します。 実際のスレッド・セットは、結果の構築中にも動的に変化する可能性があるため、返されるコレクションは最善の努力を払った上での推定に過ぎません。 返されるコレクションの要素には、特定の順序は存在しません。 このメソッドは、より包括的な監視機能を提供するサブクラスの構築を容易にする目的で設計されています。
      戻り値:
      スレッドのコレクション
    • toString

      public String toString()
      セマフォおよびその状態を識別する文字列を返します。 状態は括弧で囲まれ、文字列"Permits ="に続いてパーミットの数が含まれます。
      オーバーライド:
      toString 、クラス:  Object
      戻り値:
      このセマフォおよびその状態を識別する文字列