天天看點

并發:控制并發線程數的Semaphore(信号量)。應用場景其他方法

Semaphore是用來控制同時通路特定資源的線程數量,他通過協調各個線程,以保證合理的使用公平資源。

多年以來,我都覺得從字面上很難了解Semaphore所表達的含義,隻能把他比作是控制流量的紅綠燈。比如xx馬路要限制流量,隻允許同時有一百兩車在這條路上行使,其他的都必須在路口等待,是以前一百輛車會看到綠燈,可以開進這條馬路,後面的車會看到紅燈,不能駛入xx馬路,但是如果前一百輛中有5輛車已經離開了xx馬路,那麼後面就允許有5輛車駛入馬路,這個例子裡說的車就是線程,駛入馬路就表示線程在執行,離開馬路就表示線程執行完成,看見紅燈就表示線程被阻塞,不能執行。

應用場景

Semaphore可以用于做流量控制,特别是公用資源有限的應用場景,比如資料庫連接配接。假如有一個需求,要讀取幾萬個檔案的資料,因為都是IO密集型任務,我們可以啟動幾十個線程并發的讀取,但是如果讀到記憶體後,還需要存儲到資料庫中,而資料庫的連接配接數隻有10個,這時我們必須控制隻有10個線程同時擷取資料庫連接配接儲存資料,否則會報錯無法擷取資料庫連接配接。這個時候,就可以使用Semaphore來做流量控制,如下所示。

public class SemaphoreTest {
	private static final int THREAD_COUNT=30;
	private static ExecutorService threadPool =Executors.newFixedThreadPool(THREAD_COUNT);
	private static Semaphore s = new Semaphore(10);
	
	public static void main(String[] args) {
		for (int i = 0; i < THREAD_COUNT; i++) {
			threadPool.execute(new Runnable() {
				@Override
				public void run() {
					try {
						s.acquire();
						System.out.println("save data");
						s.release();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			});
		}
		threadPool.shutdown();
	}
}
           

在代碼中,雖然有30個線程在執行,但是隻允許10個并發執行。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個許可證,是個prottected方法。
  • Collection<Thread> getQueuedThreads():傳回所有等待擷取許可證的線程集合,是個protected方法。