java Semaphere可類比作業系統信号量,硬體資源如IO、記憶體、磁盤等都是有固定量的,多個程式需要競争這些資源,沒有資源就需要被挂起。
類和方法摘要
構造函數:
public Semaphore(int permits):建立具有給定的許可數和非公平的公平設定的 Semaphore。
-參數 :
permits - 初始的可用許可數目。此值可能為負數,在這種情況下,必須在授予任何擷取前進行釋放。
public Semaphore(int permits, boolean fair):建立具有給定的許可數和給定的公平設定的 Semaphore。
-參數:
permits - 初始的可用許可數目。此值可能為負數,在這種情況下,必須在授予任何擷取前進行釋放。
fair - 如果此信号量保證在争用時按先進先出的順序授予許可,則為 true;否則為 false。
常用方法:
void acquire():從此信号量擷取一個許可,在提供一個許可前一直将線程阻塞,否則線程被中斷。
void release():釋放一個許可,将其傳回給信号量。
int availablePermits():傳回此信号量中目前可用的許可數。
boolean hasQueuedThreads():查詢是否有線程正在等待擷取。
使用技巧
将信号量初始化為 1,使得它在使用時最多隻有一個可用的許可,進而可用作一個互相排斥的鎖。這通常也稱為二進制信号量,因為它隻能有兩種狀态:一個可用的許可,或零個可用的許可。按此方式使用時,二進制信号量具有某種屬性(與很多 Lock 實作不同),即可以由線程釋放“鎖”,而不是由所有者(因為信号量沒有所有權的概念)。在某些專門的上下文(如死鎖恢複)中這會很有用。
對比線程池
線程池控制的是線程數量,而信号量控制的是并發數量,雖然說看起來一樣,但兩者還是有差別的。
信号量類似于鎖機制,信号量的調用,當達到數量後,線程還是存在的,隻是被挂起了而已。而線程池,同時執行的線程數量是固定的,超過了數量的隻能等待。
代碼示例
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 service = Executors.newCachedThreadPool();
final Semaphore sp = new Semaphore();
for(int i=;i<;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
sp.acquire();
} catch (InterruptedException e1) {
e1.printStackTrace();
}
System.out.println("線程" + Thread.currentThread().getName() +
"進入,目前已有" + (-sp.availablePermits()) + "個并發");
try {
Thread.sleep((long)(Math.random()*));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("線程" + Thread.currentThread().getName() +
"即将離開");
sp.release();
//下面代碼有時候執行不準确,因為其沒有和上面的代碼合成原子單元
System.out.println("線程" + Thread.currentThread().getName() +
"已離開,目前已有" + (-sp.availablePermits()) + "個并發");
}
};
service.execute(runnable);
}
}
}
輸出:
線程pool--thread-進入,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-進入,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
線程pool--thread-即将離開
線程pool--thread-已離開,目前已有個并發
可以同時有3個線程執行sp.acquire()到sp.release()之間的代碼。