Java多執行緒-新特徵-訊號量Semaphore
簡介
訊號量(Semaphore),有時被稱為訊號燈,是在多執行緒環境下使用的一種設施, 它負責協調各個執行緒, 以保證它們能夠正確、合理的使用公共資源。
概念
Semaphore分為單值和多值兩種,前者只能被一個執行緒獲得,後者可以被若干個執行緒獲得。
以一個停車場運作為例。為了簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這時如果同時來了五輛車,看門人允許其中三輛不受阻礙的進入,然後放下車攔,剩下的車則必須在入口等待,此後來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知後,開啟車攔,放入一輛,如果又離開兩輛,則又可以放入兩輛,如此往復。
在這個停車場系統中,車位是公共資源,每輛車好比一個執行緒,看門人起的就是訊號量的作用。
更進一步,訊號量的特性如下:訊號量是一個非負整數(車位數),所有通過它的執行緒(車輛)都會將該整數減一(通過它當然是為了使用資源),當該整數值為零時,所有試圖通過它的執行緒都將處於等待狀態。在訊號量上我們定義兩種操作: Wait(等待) 和 Release(釋放)。 當一個執行緒呼叫Wait(等待)操作時,它要麼通過然後將訊號量減一,要麼一直等下去,直到訊號量大於一或超時。Release(釋放)實際上是在訊號量上執行加操作,對應於車輛離開停車場,該操作之所以叫做“釋放”是因為加操作實際上是釋放了由訊號量守護的資源。
在java中,還可以設定該訊號量是否採用公平模式,如果以公平方式執行,則執行緒將會按到達的順序(FIFO)執行,如果是非公平,則可以後請求的有可能排在佇列的頭部。
JDK中定義如下:
Semaphore(int permits, boolean fair)
建立具有給定的許可數和給定的公平設定的Semaphore。
Semaphore當前在多執行緒環境下被擴放使用,作業系統的訊號量是個很重要的概念,在程序控制方面都有應用。Java併發庫Semaphore 可以很輕鬆完成訊號量控制,Semaphore可以控制某個資源可被同時訪問的個數,通過 acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。比如在Windows下可以設定共享檔案的最大客戶端訪問個數。
Semaphore實現的功能就類似廁所有5個坑,假如有10個人要上廁所,那麼同時只能有多少個人去上廁所呢?同時只能有5個人能夠佔用,當5個人中 的任何一個人讓開後,其中等待的另外5個人中又有一個人可以佔用了。另外等待的5個人中可以是隨機獲得優先機會,也可以是按照先來後到的順序獲得機會,這取決於構造Semaphore物件時傳入的引數選項。單個訊號量的Semaphore物件可以實現互斥鎖的功能,並且可以是由一個執行緒獲得了“鎖”,再由另一個執行緒釋放“鎖”,這可應用於死鎖恢復的一些場合。
package cn.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; /** * 訊號量 * * @author 林計欽 * @version 1.0 2013-7-25 下午02:03:40 */ public class SemaphoreTest { public static void main(String[] args) { // 執行緒池 ExecutorService exec = Executors.newCachedThreadPool(); // 只能5個執行緒同時訪問 final Semaphore semp = new Semaphore(5); // 模擬20個客戶端訪問 for (int index = 0; index < 50; index++) { final int NO = index; Runnable run = new Runnable() { public void run() { try { // 獲取許可 semp.acquire(); System.out.println("Accessing: " + NO); Thread.sleep((long) (Math.random() * 10000)); // 訪問完後,釋放 semp.release(); //availablePermits()指的是當前訊號燈庫中有多少個可以被使用 System.out.println("-----------------" + semp.availablePermits()); } catch (InterruptedException e) { e.printStackTrace(); } } }; exec.execute(run); } // 退出執行緒池 exec.shutdown(); } }
Accessing: 0 Accessing: 1 Accessing: 2 Accessing: 4 Accessing: 6 Accessing: 8 -----------------0 -----------------1 Accessing: 3 -----------------1 Accessing: 5 Accessing: 9 -----------------0 -----------------1 Accessing: 7 Accessing: 10 -----------------0 -----------------1 Accessing: 11 -----------------1 Accessing: 12 -----------------1 Accessing: 13 Accessing: 14 -----------------0 -----------------1 Accessing: 15 -----------------0 Accessing: 16 -----------------1 Accessing: 17 -----------------1 Accessing: 18 -----------------1 Accessing: 19 -----------------0 Accessing: 20 Accessing: 21 -----------------0 Accessing: 22 -----------------0 -----------------1 Accessing: 23 -----------------1 Accessing: 24 -----------------0 Accessing: 25 Accessing: 26 -----------------0 -----------------1 Accessing: 27 -----------------1 Accessing: 28 -----------------1 Accessing: 29 Accessing: 30 -----------------0 -----------------1 Accessing: 31 -----------------1 Accessing: 32 -----------------1 Accessing: 33 -----------------1 Accessing: 34 Accessing: 35 -----------------0 -----------------1 Accessing: 36 -----------------1 Accessing: 37 -----------------1 Accessing: 38 -----------------1 Accessing: 39 -----------------1 Accessing: 40 Accessing: 41 -----------------0 -----------------1 Accessing: 42 Accessing: 43 -----------------0 Accessing: 44 -----------------0 -----------------1 Accessing: 45 -----------------1 Accessing: 46 -----------------1 Accessing: 47 -----------------1 Accessing: 48 -----------------1 Accessing: 49 -----------------1 -----------------2 -----------------3 -----------------4 -----------------5