簡単な紹介
セマフォは、同じ時間内にリソースへのアクセスの数を制御することができ、フロー制御を実装するために使用されるセマフォに変換され、同期化支援です。
かどうかSynchroniezdまたはReentrantLockのは、一度だけのスレッドがリソースにアクセスできるようにするが、セマフォは、複数のスレッドが同時に特定のリソースへのアクセスを指定することができます。
コンストラクタはセマフォ持って、あなたはint型の整数を渡すことがn個、最大n n個を超えた場合、スレッドは、このコードブロックを完了するまでアクセスできるスレッドは、待ちをしてください、次のスレッドでのコードの一部を表し、再入国。
定義2つのセマフォ操作:
- 取得(GET):スレッドの呼び出しが操作を取得すると、それはどちらかに成功セマフォ(信号マイナス1)を取得し、またはスレッドがセマフォ、またはタイムアウトを解放するまで待機し続け、内部セマフォをして待機キューを維持しますこれらは、中断されたスレッドに格納されています。
- リリース(リリース)は、実際に+1の値をセマフォし、その後Sepmaphoreの任意のインスタンスに対応するキューを待っている待機中のスレッドを起動します。
シナリオ
信号は、主に2つの目的があります:
- 共有資源の相互に排他的な使用の装置。
- 同時スレッドの数を制御するために使用します。
例
次の例:5つのスレッド三個の駐車スペースをつかむが、3つのグラブ駐車スペースのスレッドなど、他のスレッドの唯一最大グラブ駐車スペースに、セマフォを解放します。
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 5; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
semaphore.acquire();//申请资源
System.out.println(Thread.currentThread().getName()+"抢到车位");
ThreadUtil.sleep(RandomUtil.randomInt(1000,5000));
System.out.println(Thread.currentThread().getName()+"归还车位");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
//释放资源
semaphore.release();
}
}
},"线程"+i).start();
}
}
复制代码
注意事項
- Semaphore.acquire()とSemaphore.release()が常にペアで使用され、この時点では、アプリケーションコード自体によってそれを保証するために必要されています。
- ケースSemaphore.release()呼び出しは、最終的に、ブロック内に配置する必要があり、取得した現在のスレッド量信号が返されないことができ、アプリケーション・コードを避けるために、例外でした。
- パラメータの値がセマフォコンストラクタを許可する場合はセマフォ、ミューテックスを作成するために対応する、1に設定されている。他のミューテックス異なる、このスレッドがミューテックスを解放するロックが別のスレッドによって保持可能次のスレッドが場合ではないSemaphore.acquire()を実行することができるのでSemaphore.release、対応します()。
- デフォルトでは、非セマフォ公平性スケジューリング戦略を採用しました。
原則
abstract static class Sync extends AbstractQueuedSynchronizer {
//省略
}
复制代码
セマフォクラス内部使用同期同期底部または使用AQSがセマフォを取得する際、公正政策を採用するかどうかを指定するために使用.Sync 2つの実装クラスNonfairSyncとFairSyncを実装して、シンクは、AbstractQueuedSynchronizer継承されます。
初期化の方法
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
Sync(int permits) {
setState(permits);
}
复制代码
デフォルト以外のセマフォ公平性ポリシーは、上記のように必要な公平性ポリシーを構成するために2つの引数のコンストラクタで使用することができる場合、セマフォオブジェクト。
許可パラメータは、セマフォの数は、現在保持されて表すAQS値の状態に渡されます。
ボイド取得()メソッド
現在のスレッドの目的は、方法は、セマフォ資源を獲得することで呼び出します。
数は、現在の信号量よりも大きい場合には0であり、セマフォの現在のカウントが1、方法戻るだけ減分されます。それ以外の場合は、セマフォ0の現在の数ならば、現在のスレッドは、キューをブロックしAQSを配置されます。別のスレッドの呼び出しは、現在のスレッドの割り込み、現在のスレッド()メソッドを中断すると、現在のスレッドが例外:InterruptedException例外リターンがスローされます。
//Semaphore方法
public void acquire() throws InterruptedException {
//传递参数为1,说明要获取1个信号量资源
sync.acquireSharedInterruptibly(1);
}
//AQS的方法
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
//(1)如果线程被中断,则抛出中断异常
if (Thread.interrupted())
throw new InterruptedException();
//(2)否则调用Sync子类方法尝试获取,这里根据构造函数确定使用公平策略
if (tryAcquireShared(arg) < 0)
//如果获取失败则放入阻塞队列.然后再次尝试,如果使用则调用park方法挂起当前线程
doAcquireSharedInterruptibly(arg);
}
复制代码
割り込みに応答します同期acquireSharedlnterruptibly方法、内のコード、獲得()の呼び出しからわかるように(現在のスレッドが中断された場合は、割り込み例外がスローされました)。セマフォAQSリソースを取得する試みがので、ここで二つの方法に記載されている、方法tryAcquireShared同期サブクラスによって達成されます。
最初TryAcquireShared非エクイティ戦略NonfairSyncクラスコードの方法を議論し、次のとおりです。
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
//获取当前信号量值
int available = getState();
//计算当前剩余值
int remaining = available - acquires;
//如果当前剩余值小于0或则CAS设置成功则返回
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
复制代码
電流信号の大きさ(使用可能)を得るために上記のコードは、(取得)得られる値を減算し、残りの信号(残り)の数を取得すると、残留値は、0の数が需要を満たすために信号電流の量を示す未満である場合次いで、直接バック負は、現在のスレッドは、キューをブロックAQSが中断され配置されます。残差値が0より大きい場合、CAS動作は、残りの信号の大きさの現在値を設定し、残りの値を返すことができます。
また、原因非NonFairSyncの公平なアクセスに、最初の呼び出しのAQUIRE方法が必ずしも良く、後のものよりもセマフォスレッドを得るために、という最初のセマフォを取得します。
スレッドAの最初の呼び出しのAQUIRE()メソッドは、セマフォを取得する場合、次のシナリオを検討するが、セマフォの現在の数は、AQS Aブロッキングキュー、スレッドが配置され、0です。他のスレッドがセマフォを取得していない場合、スレッドC呼び出しリリース()メソッドリリースはセマフォ、スレッドAが活性化されるいくつかの時間の後、次いで、セマフォを獲得するが、スレッドCであればセマフォのリリース後、スレッドCは、スレッド、AQUIREメソッドを呼び出し、Cにスレッドこのセマフォ資源を競います。スレッドAの前にスレッド後nonfairTryAcquireSharedコードCから見た非公平性ポリシーは、活性化することができ、又は第1の信号の活性化がAスレッドの量で得られた場合、スレッドは、このモードと現在の要求にスレッドをブロックされていますこれは、最初の最初の務め来るポリシーに従うのではなく、競争関係です。
ここを見てFairSyncクラスの公平性、公平性を確保する方法です。
protected int tryAcquireShared(int acquires) {
for (;;) {
//查询是否当前线程节点的前驱节点也在等待获取该资源,有的话直接返回
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
复制代码
可視公正またはhasQueuedPredecessorsを確保するために、この機能に依存しています。セマフォの公平性ポリシーは、現在のスレッドノード前駆体ノードがリソースを取得するために待機しているされているかどうかので、そのために取得権限を放棄する場合は、現在のスレッドは、AQSがブロックされたキューに配置される、または彼はなるだろう。
無効取得(int型の許可)メソッド
前者の許可を取得している間のみ、信号の大きさを取得する必要方法及び取得()異なる方法、。
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
}
复制代码
空acquireUninterruptibly()方法
このメソッドの取得は、()中断の方法で応答、である、リソースへのacquireUninterruptiblyアクセスが(ブロックされた後に含まれている)現在のスレッドの呼び出し、現在のスレッド()メソッドのセットを割り込み、他のスレッドの呼び出しをしないことを除いて、似ています現在のスレッドの割り込みフラグは、その後、現在のスレッドは、例外戻り、IllegalArgumentExceptionをスローしません。
public void acquireUninterruptibly(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireShared(permits);
}
复制代码
空のリリース()メソッド
呼び出しメソッドがAQSブロッキングキューに置かれるから遮断されるAQUIREがあるので、現在のスレッドは、信号が選択される場合は、このメソッドの役割を満たすことエクイティ戦略の量の数に応じた信号電流セマフォオブジェクトの大きさが1だけ増加することですスレッド起動、信号利得のちょうど量を得るために、活性化スレッドの試み。
public void release() {
//(1)arg=1
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
//(2)尝试释放资源
if (tryReleaseShared(arg)) {
//(3)资源释放成功则调用park方法唤醒AQS队列里面最先挂起的线程
doReleaseShared();
return true;
}
return false;
}
protected final boolean tryReleaseShared(int releases) {
for (;;) {
//获取当前信号量值
int current = getState();
//将当前信号量值增加releases,这里为增加1
int next = current + releases;
//移除处理
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
//使用CAS保证更新信号量值的原子性
if (compareAndSetState(current, next))
return true;
}
}
复制代码
コード・リリース()によって - sync.releaseShared>(1)、各リリース方法は、信号1の大きさを増加させる、見ることができ、tryReleaseShared方法が無限ループであり、CASは確実にするために使用されるセマフォ操作アトミックインクリメント1の解除方法.tryReleaseShared法後の信号の大きさが正常に取得メソッドの呼び出しがスレッドをブロックするのでAQS呼をアクティブにする方法であるコード(3)を、実行増加します。
空のリリース(int型の許可)方法
このメソッドのパラメータなしのリリース方法と異なる、前者許可すべての呼び出しは、後者インクリメントさLに基づいて、元の信号の大きさを増大させます。
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
}
复制代码
また、ここでsync.releaseSharedスレッドを結合することなく、セマフォをスレッド、セマフォによって共有されて固定されていることを示す、方法を共有して、見ることができ、複数のスレッドが同じ時間量にすることなく、CAS信号の値を更新するために使用することができます障害物。