類 Semaphore
public class Semaphoreextends Objectimplements Serializable一個計數(shù)信號量。
從概念上講,信號量維護了一個許可集。
如有必要,在許可可用前會阻塞每一個 acquire(),然后再獲取該許可。每個 release() 添加一個許可,從而可能釋放一個正在阻塞的獲取者。
但是,不使用實際的許可對象,Semaphore 只對可用許可的號碼進行計數(shù),并采取相應(yīng)的行動。
Semaphore 通常用于限制可以訪問某些資源(物理或邏輯的)的線程數(shù)目。例如,下面的類使用信號量控制對內(nèi)容池的訪問:
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 =

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;
}
}
獲得一項前,每個線程必須從信號量獲取許可,從而保證可以使用該項。
該線程結(jié)束后,將項返回到池中并將許可返回到該信號量,從而允許其他線程獲取該項。
注意,調(diào)用 acquire() 時無法保持同步鎖,因為這會阻止將項返回到池中。
信號量封裝所需的同步,以限制對池的訪問,這同維持該池本身一致性所需的同步是分開的。
將信號量初始化為 1,使得它在使用時最多只有一個可用的許可,從而可用作一個相互排斥的鎖。
這通常也稱為二進制信號量,因為它只能有兩種狀態(tài):一個可用的許可,或零個可用的許可。
按此方式使用時,二進制信號量具有某種屬性(與很多 Lock 實現(xiàn)不同),即可以由線程釋放“鎖”,而不是由所有者(因為信號量沒有所有權(quán)的概念)。
在某些專門的上下文(如死鎖恢復(fù))中這會很有用。
此類的構(gòu)造方法可選地接受一個公平 參數(shù)。當設(shè)置為 false 時,此類不對線程獲取許可的順序做任何保證。
特別地,闖入 是允許的,也就是說可以在已經(jīng)等待的線程前為調(diào)用 acquire() 的線程分配一個許可,
從邏輯上說,就是新線程將自己置于等待線程隊列的頭部。
當公平設(shè)置為 true 時,信號量保證對于任何調(diào)用獲取方法的線程而言,都按照處理它們調(diào)用這些方法的順序(即先進先出;FIFO)來選擇線程、獲得許可。
注意,F(xiàn)IFO 排序必然應(yīng)用到這些方法內(nèi)的指定內(nèi)部執(zhí)行點。
所以,可能某個線程先于另一個線程調(diào)用了 acquire,但是卻在該線程之后到達排序點,并且從方法返回時也類似。
還要注意,非同步的 tryAcquire 方法不使用公平設(shè)置,而是使用任意可用的許可。
通常,應(yīng)該將用于控制資源訪問的信號量初始化為公平的,以確保所有線程都可訪問資源。
為其他的種類的同步控制使用信號量時,非公平排序的吞吐量優(yōu)勢通常要比公平考慮更為重要。
此類還提供便捷的方法來同時 acquire 和釋放多個許可。
小心,在未將公平設(shè)置為 true 時使用這些方法會增加不確定延期的風險。
內(nèi)存一致性效果:線程中調(diào)用“釋放”方法(比如 release())之前的操作 happen-before 另一線程中緊跟在成功的“獲取”方法(比如 acquire())之后的操作。
acquire
public void acquire()
throws InterruptedException從此信號量獲取一個許可,在提供一個許可前一直將線程阻塞,否則線程被中斷。
獲取一個許可(如果提供了一個)并立即返回,將可用的許可數(shù)減 1。
如果沒有可用的許可,則在發(fā)生以下兩種情況之一前,禁止將當前線程用于線程安排目的并使其處于休眠狀態(tài):
某些其他線程調(diào)用此信號量的 release() 方法,并且當前線程是下一個要被分配許可的線程;或者
其他某些線程中斷當前線程。
如果當前線程:
被此方法將其已中斷狀態(tài)設(shè)置為 on ;或者
在等待許可時被中斷。
則拋出 InterruptedException,并且清除當前線程的已中斷狀態(tài)。
拋出:
InterruptedException - 如果當前線程被中斷
release
public void release()釋放一個許可,將其返回給信號量。
釋放一個許可,將可用的許可數(shù)增加 1。
如果任意線程試圖獲取許可,則選中一個線程并將剛剛釋放的許可給予它。然后針對線程安排目的啟用(或再啟用)該線程。
不要求釋放許可的線程必須通過調(diào)用 acquire() 來獲取許可。通過應(yīng)用程序中的編程約定來建立信號量的正確用法。
應(yīng)用實例:
/*
*
Semaphore的作用類似Lock的功能,不同的是Semaphore的構(gòu)造函數(shù)中可以傳入一個int型的參數(shù),用來確定創(chuàng)建一個多大的通道。
Lock一次只允許一個線程進入,解鎖后才允許別的線程進入。而Semaphore可以允許多個線程同時進入,一旦有線程釋放就會空出一個位置讓另外的線程進入。
相當與Lock是一個單車道,而Semaphore是一個多車道。
*/
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService es = Executors.newCachedThreadPool();
// final Semaphore sema=new Semaphore(3); //共有3個通道
final Semaphore sema = new Semaphore(3, true); // 表示按排隊的先后順序進入
for (int i = 0; i < 10; i++) {
es.execute(new Runnable() {
@Override
public void run() {
try {
sema.acquire();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程" + Thread.currentThread().getName()
+ "已進入,當前有" + (3 - sema.availablePermits())
+ "個線程并發(fā)");
try {
Thread.sleep(new Random().nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程" + Thread.currentThread().getName()
+ "即將離開");
sema.release(); // 一個線程釋放就會允許另外的線程進入
}
});
}
es.shutdown();
}
}
運行結(jié)果:
線程pool-1-thread-2已進入,當前有2個線程并發(fā)
線程pool-1-thread-1已進入,當前有2個線程并發(fā)
線程pool-1-thread-3已進入,當前有3個線程并發(fā)
線程pool-1-thread-1即將離開
線程pool-1-thread-4已進入,當前有3個線程并發(fā)
線程pool-1-thread-4即將離開
線程pool-1-thread-5已進入,當前有3個線程并發(fā)
線程pool-1-thread-2即將離開
線程pool-1-thread-9已進入,當前有3個線程并發(fā)
線程pool-1-thread-5即將離開
線程pool-1-thread-7已進入,當前有3個線程并發(fā)
線程pool-1-thread-3即將離開
線程pool-1-thread-6已進入,當前有3個線程并發(fā)
線程pool-1-thread-6即將離開
線程pool-1-thread-8已進入,當前有3個線程并發(fā)
線程pool-1-thread-9即將離開
線程pool-1-thread-10已進入,當前有3個線程并發(fā)
線程pool-1-thread-7即將離開
線程pool-1-thread-8即將離開
線程pool-1-thread-10即將離開