java併發之Semaphore(計數訊號量)
Semaphore一個計數訊號量。從概念上講,訊號量維護了一個許可集。如有必要,在許可可用前會阻塞每一個 acquire()
,然後再獲取該許可。每個 release()
新增一個許可,從而可能釋放一個正在阻塞的獲取者。但是,不使用實際的許可物件,Semaphore
只對可用許可的號碼進行計數,並採取相應的行動。
Semaphore 通常用於限制可以訪問某些資源(物理或邏輯的)的執行緒數目。
獲得一項前,每個執行緒必須從訊號量獲取許可,從而保證可以使用該項。該執行緒結束後,將項返回到池中並將許可返回到該訊號量,從而允許其他執行緒獲取該項。注意,呼叫 acquire()
時無法保持同步鎖,因為這會阻止將項返回到池中。訊號量封裝所需的同步,以限制對池的訪問,這同維持該池本身一致性所需的同步是分開的。
將訊號量初始化為 1,使得它在使用時最多隻有一個可用的許可,從而可用作一個相互排斥的鎖。這通常也稱為二進位制訊號量,因為它只能有兩種狀態:一個可用的許可,或零個可用的許可。按此方式使用時,二進位制訊號量具有某種屬性(與很多 Lock
實現不同),即可以由執行緒釋放“鎖”,而不是由所有者(因為訊號量沒有所有權的概念)。在某些專門的上下文(如死鎖恢復)中這會很有用。
此類的構造方法可選地接受一個公平 引數。當設定為 false 時,此類不對執行緒獲取許可的順序做任何保證。特別地,闖入 是允許的,也就是說可以在已經等待的執行緒前為呼叫 acquire()
的執行緒分配一個許可,從邏輯上說,就是新執行緒將自己置於等待執行緒佇列的頭部。當公平設定為
true 時,訊號量保證對於任何呼叫獲取
方法的執行緒而言,都按照處理它們呼叫這些方法的順序(即先進先出;FIFO)來選擇執行緒、獲得許可。注意,FIFO 排序必然應用到這些方法內的指定內部執行點。所以,可能某個執行緒先於另一個執行緒呼叫了 acquire
,但是卻在該執行緒之後到達排序點,並且從方法返回時也類似。還要注意,非同步的 tryAcquire
方法不使用公平設定,而是使用任意可用的許可。
通常,應該將用於控制資源訪問的訊號量初始化為公平的,以確保所有執行緒都可訪問資源。為其他的種類的同步控制使用訊號量時,非公平排序的吞吐量優勢通常要比公平考慮更為重要。
此類還提供便捷的方法來同時 acquire
和釋放
多個許可。小心,在未將公平設定為
true 時使用這些方法會增加不確定延期的風險。
記憶體一致性效果:執行緒中呼叫“釋放”方法(比如 release()
)之前的操作 happen-before 另一執行緒中緊跟在成功的“獲取”方法(比如 acquire()
)之後的操作。
---摘自java api
下面自己寫一個入門例項
import java.util.concurrent.Semaphore;
public class SeDemo
{
public static void main(String[] args)
{
Semaphore semaphore = new Semaphore(2);
Person p1 = new Person(semaphore, "A");
p1.start();
Person p2 = new Person(semaphore, "B");
p2.start();
Person p3 = new Person(semaphore, "C");
p3.start();
}
}
class Person extends Thread
{
private Semaphore semaphore;
public Person(Semaphore semaphore, String name)
{
this.semaphore = semaphore;
setName(name);
}
@Override
public void run()
{
System.out.println(getName()+" waiting...");
try
{
this.semaphore.acquire();//獲取一個許可證
System.out.println(getName()+" serveice...");
Thread.sleep(1000);
}catch(Exception e)
{
e.printStackTrace();
}
this.semaphore.release();//釋放一個許可證
System.out.println(getName()+" over...");
}
}