モジュール 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つの許可が使用可能になった場合、その取得が最初に行われ、セマフォsがフェア・モードでないかぎり、スレッドBがそれらを取得することは保証されません。

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

導入されたバージョン:
1.5
関連項目: