Semaphore的工作原理及例項
Semaphore是一種在多執行緒環境下使用的設施,該設施負責協調各個執行緒,以保證它們能夠正確、合理的使用公共資源的設施,也是作業系統中用於控制程序同步互斥的量。Semaphore是一種計數訊號量,用於管理一組資源,內部是基於AQS的共享模式。它相當於給執行緒規定一個量從而控制允許活動的執行緒數。
1.工作原理
以一個停車場是運作為例。為了簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這時如果同時來了五輛車,看門人允許其中三輛不受阻礙的進入,然後放下車攔,剩下的車則必須在入口等待,此後來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知後,開啟車攔,放入一輛,如果又離開兩輛,則又可以放入兩輛,如此往復。這個停車系統中,每輛車就好比一個執行緒,看門人就好比一個訊號量,看門人限制了可以活動的執行緒。假如裡面依然是三個車位,但是看門人改變了規則,要求每次只能停兩輛車,那麼一開始進入兩輛車,後面得等到有車離開才能有車進入,但是得保證最多停兩輛車。對於Semaphore類而言,就如同一個看門人,限制了可活動的執行緒數。
Semaphore主要方法:
Semaphore(int permits):構造方法,建立具有給定許可數的計數訊號量並設定為非公平訊號量。
Semaphore(int permits,boolean fair):構造方法,當fair等於true時,建立具有給定許可數的計數訊號量並設定為公平訊號量。
void acquire():從此訊號量獲取一個許可前執行緒將一直阻塞。相當於一輛車佔了一個車位。
void acquire(int n):從此訊號量獲取給定數目許可,在提供這些許可前一直將執行緒阻塞。比如n=2,就相當於一輛車佔了兩個車位。
void release():釋放一個許可,將其返回給訊號量。就如同車開走返回一個車位。
void release(int n):釋放n個許可。
int availablePermits():當前可用的許可數。
2.例項講解
接下來舉個例子,就是關於每個人的個人資訊,那麼一個人佔用一個執行緒,並用Semphore類建立物件從而初始化訊號量,控制可活動的執行緒數。具體程式碼如下:
package concurrent; import java.util.concurrent.Semaphore; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.LinkedBlockingQueue; public class SemaphoreDemo { private static final Semaphore semaphore=new Semaphore(3); private static final ThreadPoolExecutor threadPool=new ThreadPoolExecutor(5,10,60,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>()); private static class InformationThread extends Thread{ private final String name; private final int age; public InformationThread(String name,int age) { this.name=name; this.age=age; } public void run() { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName()+":大家好,我是"+name+"我今年"+age+"歲當前時間為:"+System.currentTimeMillis()); Thread.sleep(1000); System.out.println(name+"要準備釋放許可證了,當前時間為:"+System.currentTimeMillis()); System.out.println("當前可使用的許可數為:"+semaphore.availablePermits()); semaphore.release(); } catch(InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { String[] name= {"李明","王五","張傑","王強","趙二","李四","張三"}; int[] age= {26,27,33,45,19,23,41}; for(int i=0;i<7;i++) { Thread t1=new InformationThread(name[i],age[i]); threadPool.execute(t1); } } }
執行上述程式結果如下:
pool-1-thread-3:大家好,我是張傑我今年33歲當前時間為:1520424000186
pool-1-thread-1:大家好,我是李明我今年26歲當前時間為:1520424000186
pool-1-thread-2:大家好,我是王五我今年27歲當前時間為:1520424000186
張傑要準備釋放許可證了,當前時間為:1520424001187
李明要準備釋放許可證了,當前時間為:1520424001187
王五要準備釋放許可證了,當前時間為:1520424001187
當前可使用的許可數為:0
當前可使用的許可數為:0
當前可使用的許可數為:0
pool-1-thread-4:大家好,我是王強我今年45歲當前時間為:1520424001187
pool-1-thread-2:大家好,我是張三我今年41歲當前時間為:1520424001187
pool-1-thread-1:大家好,我是李四我今年23歲當前時間為:1520424001187
李四要準備釋放許可證了,當前時間為:1520424002187
王強要準備釋放許可證了,當前時間為:1520424002187
當前可使用的許可數為:0
張三要準備釋放許可證了,當前時間為:1520424002187
pool-1-thread-5:大家好,我是趙二我今年19歲當前時間為:1520424002187
當前可使用的許可數為:0
當前可使用的許可數為:0
趙二要準備釋放許可證了,當前時間為:1520424003188
當前可使用的許可數為:2
以上是非公平訊號量,將建立Semaphore物件的語句改為如下語句:
private static final Semaphore semaphore=new Semaphore(3,true);
執行程式:
pool-1-thread-2:大家好,我是王五我今年27歲當前時間為:1520424286454
pool-1-thread-3:大家好,我是張傑我今年33歲當前時間為:1520424286454
pool-1-thread-1:大家好,我是李明我今年26歲當前時間為:1520424286454
pool-1-thread-1:李明要準備釋放許可證了,當前時間為:1520424287455
當前可使用的許可數為:0
pool-1-thread-2:王五要準備釋放許可證了,當前時間為:1520424287455
pool-1-thread-3:張傑要準備釋放許可證了,當前時間為:1520424287455
當前可使用的許可數為:0
當前可使用的許可數為:1
pool-1-thread-1:大家好,我是李四我今年23歲當前時間為:1520424287455
pool-1-thread-5:大家好,我是趙二我今年19歲當前時間為:1520424287455
pool-1-thread-4:大家好,我是王強我今年45歲當前時間為:1520424287455
pool-1-thread-4:王強要準備釋放許可證了,當前時間為:1520424288456
當前可使用的許可數為:0
pool-1-thread-1:李四要準備釋放許可證了,當前時間為:1520424288456
pool-1-thread-3:大家好,我是張三我今年41歲當前時間為:1520424288456
pool-1-thread-5:趙二要準備釋放許可證了,當前時間為:1520424288456
當前可使用的許可數為:0
當前可使用的許可數為:0
pool-1-thread-3:張三要準備釋放許可證了,當前時間為:1520424289456
當前可使用的許可數為:2
3.實現單例模式
將建立訊號量物件語句修改如下:
private static final Semaphore semaphore=new Semaphore(1);
執行程式,結果如下:
pool-1-thread-1:大家好,我是李明我今年26歲當前時間為:1520424379699
pool-1-thread-1:李明要準備釋放許可證了,當前時間為:1520424380700
當前可使用的許可數為:0
pool-1-thread-2:大家好,我是王五我今年27歲當前時間為:1520424380700
pool-1-thread-2:王五要準備釋放許可證了,當前時間為:1520424381701
當前可使用的許可數為:0
pool-1-thread-3:大家好,我是張傑我今年33歲當前時間為:1520424381701
pool-1-thread-3:張傑要準備釋放許可證了,當前時間為:1520424382702
當前可使用的許可數為:0
pool-1-thread-4:大家好,我是王強我今年45歲當前時間為:1520424382702
pool-1-thread-4:王強要準備釋放許可證了,當前時間為:1520424383702
當前可使用的許可數為:0
pool-1-thread-5:大家好,我是趙二我今年19歲當前時間為:1520424383702
pool-1-thread-5:趙二要準備釋放許可證了,當前時間為:1520424384702
當前可使用的許可數為:0
pool-1-thread-1:大家好,我是李四我今年23歲當前時間為:1520424384702
pool-1-thread-1:李四要準備釋放許可證了,當前時間為:1520424385702
當前可使用的許可數為:0
pool-1-thread-2:大家好,我是張三我今年41歲當前時間為:1520424385702
pool-1-thread-2:張三要準備釋放許可證了,當前時間為:1520424386703
當前可使用的許可數為:0
如上可知,如果將給定許可數設定為1,就如同一個單例模式,即單個停車位,只有一輛車進,然後這輛車出來後,下一輛車才能進。
4.總結
Semaphore主要用於控制當前活動執行緒數目,就如同停車場系統一般,而Semaphore則相當於看守的人,用於控制總共允許停車的停車位的個數,而對於每輛車來說就如同一個執行緒,執行緒需要通過acquire()方法獲取許可,而release()釋放許可。如果許可數達到最大活動數,那麼呼叫acquire()之後,便進入等待佇列,等待已獲得許可的執行緒釋放許可,從而使得多執行緒能夠合理的執行。