Semaphore是用來限制訪問特定資源的併發線程的數量。併發
相對於內置鎖synchronized和重入鎖ReentrantLock的互斥性來講,Semaphore能夠容許多個線程同時訪問共享資源。ide
Semaphore(int permits):建立Semaphore,並指定許可證的數量。(公平策略爲非公平)ui
Semaphore(int permits, boolean fair):建立Semaphore,並指定許可證的數量和公平策略。spa
acquire():從Semaphore中獲取一個許可證,若是獲取不到則阻塞等待,直到其餘線程釋放了一個許可證或者當前線程被中斷。線程
acquire(int permits):從Semaphore中獲取指定數量的許可證,若是獲取不到則阻塞等待,直到其餘線程釋放了對應數量的許可證或者當前線程被中斷。code
acquireUninterruptibly():從Semaphore中獲取一個許可證,若是獲取不到則阻塞等待,直到其餘線程釋放了一個許可證。(不響應中斷)圖片
tryAcquire():嘗試從Semaphore中獲取一個許可證,獲取成功則返回true,獲取失敗則返回false,不會進行等待。(不受公平策略的影響,許可證可用則當即得到)ci
tryAcquire(long timeout, TimeUnit unit):嘗試從Semaphore中獲取一個許可證,獲取成功則返回true,獲取失敗則等待指定的時間,直到等待時間結束仍是沒有獲取到許可證則返回false。資源
release():釋放一個許可證。get
release(int permits):釋放指定數量的許可證。
總共有5個許可證,最早獲取到許可證的5個線程開始執行任務,沒獲取到的線程進入等待狀態,直到獲取到許可證的線程釋放許可證後,再獲取許可證執行任務。
public class Demo {
public static void main(String[] args) {
//建立許可證數量爲5的Semaphore
Semaphore semaphore = new Semaphore(5);
Runnable runnable = () -> {
String threadName = Thread.currentThread().getName();
try{
//獲取一個許可證
semaphore.acquire();
System.out.println(threadName + "執行任務...");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//釋放一個許可證
semaphore.release();
}
};
ExecutorService executorService = Executors.newFixedThreadPool(10);
for(int i = 0; i < 10; i++){
executorService.execute(runnable);
}
executorService.shutdown();
}
}
/* 開始輸出:
* pool-1-thread-1執行任務...
* pool-1-thread-5執行任務...
* pool-1-thread-6執行任務...
* pool-1-thread-7執行任務...
* pool-1-thread-3執行任務...
* 三秒後輸出:
* pool-1-thread-4執行任務...
* pool-1-thread-8執行任務...
* pool-1-thread-2執行任務...
* pool-1-thread-10執行任務...
* pool-1-thread-9執行任務...
*/
使用Semaphore實現互斥只須要將許可證數量設置爲1,這樣就能夠保證只有一個線程能獲取到許可證。
Semaphore semaphore = new Semaphore(1);
相比內置鎖synchronized和重入鎖ReentrantLock,使用Semaphore實現互斥有個明顯的缺點:不可重入,沒有釋放許可證的狀況下,再次調acquire方法將致使死鎖。
示例:
public class Demo {
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(1);
Runnable runnable = () -> {
String threadName = Thread.currentThread().getName();
try {
//獲取一個許可證
semaphore.acquire();
System.out.println(threadName + "執行任務A...");
semaphore.acquire();
System.out.println(threadName + "執行任務B...");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
//釋放一個許可證
semaphore.release();
}
};
new Thread(runnable).start();
}
}
/*
* 輸出結果:
* Thread-0執行任務A...
*/
「執行任務B」永遠不會打印,由於許可證只有一個,第二次acquire方法的調用會由於沒法獲取到許可證而一直阻塞。