Samaphore可以維護當前訪問自身的線程個數,並提供了同步機制。使用Samaphore可以控制同時訪問資源的線程個數,例如,實現一個文件允許的併發訪問。
從概念上講,信號量維護了一個許可集。如有必要,在許可可用前會阻塞每一個 acquire()
,然後再獲取該許可。每個
release()
添加一個許可,從而可能釋放一個正在阻塞的獲取者。但是,不使用實際的許可對象,Semaphore
只對可用許可的號碼進行計數,並採取相應的行動。
將信號量初始化爲 1,使得它在使用時最多隻有一個可用的許可,從而可用作一個相互排斥的鎖。這通常也稱爲二進制信號量,因爲它只能有兩種狀態:一個可用的許可,或零個可用的許可。按此方式使用時,二進制信號量具有某種屬性(與很多Lock
實現不同),即可以由線程釋放“鎖”,而不是由所有者(因爲信號量沒有所有權的概念)。在某些專門的上下文(如死鎖恢復)中這會很有用。
此類的構造方法可選地接受一個公平 參數。當設置爲 false 時,此類不對線程獲取許可的順序做任何保證。特別地,闖入 是允許的,也就是說可以在已經等待的線程前爲調用acquire()
的線程分配一個許可,從邏輯上說,就是新線程將自己置於等待線程隊列的頭部。當公平設置爲 true 時,信號量保證對於任何調用獲取
方法的線程而言,都按照處理它們調用這些方法的順序(即先進先出;FIFO)來選擇線程、獲得許可。注意,FIFO
排序必然應用到這些方法內的指定內部執行點。所以,可能某個線程先於另一個線程調用了acquire
,但是卻在該線程之後到達排序點,並且從方法返回時也類似。還要注意,非同步的
tryAcquire
方法不使用公平設置,而是使用任意可用的許可。
通常,應該將用於控制資源訪問的信號量初始化爲公平的,以確保所有線程都可訪問資源。爲其他的種類的同步控制使用信號量時,非公平排序的吞吐量優勢通常要比公平考慮更爲重要。
此類還提供便捷的方法來同時 acquire
和釋放
多個許可。小心,在未將公平設置爲 true 時使用這些方法會增加不確定延期的風險。
此類還提供便捷的方法來同時 acquire
和釋放
多個許可。小心,在未將公平設置爲 true 時使用這些方法會增加不確定延期的風險。
內存一致性效果:線程中調用“釋放”方法(比如 release()
)之前的操作
happen-before 另一線程中緊跟在成功的“獲取”方法(比如 acquire()
)之後的操作。
示例:
public class SemaphoreTest {
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore(3);
for(int i=0;i<10;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
sp.acquire();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("線程" + Thread.currentThread().getName() +
"進入,當前已有" + (3-sp.availablePermits()) + "個併發");
try {
Thread.sleep((long)(Math.random()*10000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程" + Thread.currentThread().getName() +
"即將離開");
sp.release();
//下面代碼有時候執行不準確,因爲其沒有和上面的代碼合成原子單元
System.out.println("線程" + Thread.currentThread().getName() +
"已離開,當前已有" + (3-sp.availablePermits()) + "個併發");
}
};
service.execute(runnable);
}
}
}
結果:
線程pool-1-thread-2進入,當前已有2個併發
線程pool-1-thread-1進入,當前已有2個併發
線程pool-1-thread-3進入,當前已有3個併發
線程pool-1-thread-2即將離開
線程pool-1-thread-2已離開,當前已有2個併發
線程pool-1-thread-5進入,當前已有3個併發
線程pool-1-thread-5即將離開
線程pool-1-thread-5已離開,當前已有2個併發
線程pool-1-thread-4進入,當前已有3個併發
線程pool-1-thread-3即將離開
線程pool-1-thread-3已離開,當前已有2個併發
線程pool-1-thread-7進入,當前已有3個併發
線程pool-1-thread-4即將離開
線程pool-1-thread-4已離開,當前已有2個併發
線程pool-1-thread-9進入,當前已有3個併發
線程pool-1-thread-7即將離開
線程pool-1-thread-7已離開,當前已有2個併發
線程pool-1-thread-8進入,當前已有3個併發
線程pool-1-thread-1即將離開
線程pool-1-thread-1已離開,當前已有2個併發
線程pool-1-thread-6進入,當前已有3個併發
線程pool-1-thread-6即將離開
線程pool-1-thread-6已離開,當前已有2個併發
線程pool-1-thread-10進入,當前已有3個併發
線程pool-1-thread-8即將離開
線程pool-1-thread-8已離開,當前已有2個併發
線程pool-1-thread-10即將離開
線程pool-1-thread-10已離開,當前已有1個併發
線程pool-1-thread-9即將離開
線程pool-1-thread-9已離開,當前已有0個併發
示例出自傳智播客 張孝祥 java多線程與併發庫高級應用。