Semaphore(信號量)使用來控制通知訪問特定資源的線程數量,它通過協調各個線程,以保證合理的使用公共資源。
我們可以這么理解Semaphore,比如一個廁所只有6個坑,同時只能滿足6個人上廁所(變態除外),其他人想蹲坑,只能排隊等待,如果有人從廁所出來,后面的一個人就可以進去。在這個例子中人就是線程,蹲坑表示線程在執行,離開表示線程執行完畢,而坑的數量就表示Semaphore的個數。
一.Semaphore的應用場景
Semaphore可以用於做流量控制,特別是公用資源有限的應用場景,比如數據庫連接。假如有一個需求,要讀取幾萬個文件的數據,因為都是IO密集型任務,我們可以啟動幾十個線程並發地讀取,但是如果讀到內存后,還需要存儲到數據庫中,而數據庫的連接數只有10個,這時我們必須控制只有10個線程同時獲取數據庫連接保存數據,否則會報錯無法獲取數據庫連接。這個時候,就可以使用Semaphore來做流量控制。簡單實現如下:
public class SemaphoreTest { private static final int THREAD_COUNT = 30; private static ExecutorService threadPool = Executors.newFixedThreadPool(THREAD_COUNT); //只有10個數據庫鏈接,這里創建10個信號量 private static Semaphore semaphore = new Semaphore(10); public static void main(String[] args){ boolean shutDownThreadPool = false; for(int index = 0; index < THREAD_COUNT ;index++){ threadPool.execute(new Runnable() { @Override public void run() { try { //獲取一個信號 semaphore.acquire(); //執行操作 System.out.println("wait for write data..."); Thread.sleep(1000); //釋放信號 semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } } }); if(index == THREAD_COUNT-1){ shutDownThreadPool = true; } } while(!shutDownThreadPool){ threadPool.shutdown(); } } }
上述代碼中雖然創建了30個線程,但是同時只能有是個線程在並發的執行。Semaphore的構造方法Semaphore(int permits)接受一個整型的數字,表示可用的許可證數量。Semaphore(10)表示允許10個線程獲取許可證,也就是最大並發數是10。Semaphore的用法也很簡單,首先線程使用Semaphore的acquire()方法獲取一個許可證,使用完之后調用release()方法歸還許可證。還可以用tryAcquire()方法嘗試獲取許可證。
二.Semaphore的其他方法
int availablePermits():返回當前可用的許可證數量
int getQueueLength():獲取正在等待獲取許可證的線程數量
boolean hasQueuedThreads():獲取是否還有等待獲取許可證的線程
void reducePermits(int reduction):減少reduction個許可證