前言
上節我們介紹了Java中的併發工具類CountDownLatch和Cyclicbarrier,今天我們再來說說另外兩個併發工具類:Semaphore(信號量)和Exchanger(交換者),首先我們先來說說信號量這個東西,結合我們今天所講的,我們瞭解下,這個東西到底有啥用,話不多說,讓我們開啓今天的併發之旅吧。
什麼是信號量?
信號量是用來控制同時訪問特定資源的線程數量,通過協調各個線程,以保證合理的使用公共資源。
這樣說可能大家不是很理解,那我就舉個生活中常見的場景,結合場景描述下Semaphore這個東西:
我們大家都去過大型商場,都知道商場都有自己的停車場,假設一個停車場允許的最大停車數量是200,當我們到達停車場時,會發現上面顯示着剩餘車位,如果剩餘車位>0,那麼欄杆會掃描完車牌放行,如果剩餘車位=0,則說明已沒有能使用的車位,此時就不放行,所以進入商場的車都需要排隊等候,當裏面有離開停車場,離開n輛就會放行n輛,這裏用來控制是否放行的剩餘車位數就可以理解爲是一個信號量指示,成功掃描,或者拿到停車票的車子就能進入,他用來控制當前進入商場的車是否能放行。
如何使用信號量?
使用信號量我們首先要創建一個信號量
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
然後就可以調用Semaphore的相關方法,主要有如下幾個方法:
// 獲取一個信號量許可證
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 釋放獲取的信號量許可證,一次釋放一個
public void release() {
sync.releaseShared(1);
}
// 返回信號量中可以使用的許可證數
public int availablePermits() {
return sync.getPermits();
}
// 返回正在等待獲取許可證的線程數
public final int getQueueLength() {
return sync.getQueueLength();
}
// 返回Boolean值是否存在線程正在等待獲取許可證
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
// 返回所有正在等待獲取許可證的線程集合 AQS操作
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
// AQS中的源碼
public final Collection<Thread> getQueuedThreads() {
ArrayList<Thread> list = new ArrayList<Thread>();
for (Node p = tail; p != null; p = p.prev) {
Thread t = p.thread;
if (t != null)
list.add(t);
}
return list;
}
OK,下面我們看看如何使用信號量,我們使用Semaphore來模擬一個停車場停車的場景:
public class SemaphoreDemo {
public static void main(String[] args) {
// 線程池
ExecutorService exec = Executors.newCachedThreadPool();
// 假設停車場一共有停車位5個
final Semaphore semp = new Semaphore(5);
// 模擬10個車子進入停車場
for (int index = 10000; index < 10011; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 獲取許可
semp.acquire();
System.out.println("獲得停車證進場車牌號:滬A" + NO);
// 模擬一個車子在停車場停留時間
Thread.sleep((long) (Math.random() * 10000));
// 訪問完後,釋放,離開停車場
semp.release();
System.out.println("滬A" + NO+"離開停車場");
} catch (InterruptedException e) {
}
}
};
exec.execute(run);
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 退出線程池
exec.shutdown();
}
}
執行結果:
其實Semaphore也可以理解爲是實現了等待喚醒功能,你也可以使用wait/notifyAll來實現上述模擬,感興趣的小夥伴可以自己試試。