1. 程式人生 > >Java併發程式設計之Semaphore(二)

Java併發程式設計之Semaphore(二)

一.介紹
Semaphore是一種在多執行緒環境下使用的設施,該設施負責協調各個執行緒,以保證它們能夠正確、合理的使用公共資源的設施,也是作業系統中用於控制程序同步互斥的量。Semaphore是一種計數訊號量,用於管理一組資源,內部是基於AQS的共享模式。它相當於給執行緒規定一個量從而控制允許活動的執行緒數。
二.工作原理
Semaphore是計數訊號量。Semaphore管理一系列許可證。每個acquire方法阻塞,直到有一個許可證可以獲得然後拿走一個許可證;每個release方法增加一個許可證,這可能會釋放一個阻塞的acquire方法。然而,其實並沒有實際的許可證這個物件,Semaphore只是維持了一個可獲得許可證的數量。

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():當前可用的許可數。
三.示例


下面舉個例子,比如一個停車場可以停放一些車輛,如果停放滿了那後面的車就不能進來,如果沒滿則後面的車可以繼續進來,如果滿了,後面的車只能等待直到有空餘車位:
例子:一輛車佔用一個執行緒,並用Semphore類建立物件從而初始化訊號量,控制可活動的執行緒數,也就是訊號量控制車位的剩餘量,

1.當訊號量為1時,具體程式碼如下:
  private static final Semaphore semaphore=new Semaphore(1);
        public static class partCarRunable implements Runnable {
        private
final String carName; public partCarRunable(String name){ this.carName=name; } @Override public void run() { try { semaphore.acquire(); System.out.println("執行緒名稱:"+Thread.currentThread().getName()+",車型別:"+carName+",當前可用停車位:"+semaphore.availablePermits()+",當前時間為:"+System.currentTimeMillis()); semaphore.release(); System.out.println("離開:"+Thread.currentThread().getName()+","+carName+"已離場,當前可用停車位:"+semaphore.availablePermits()+",當前時間為:"+System.currentTimeMillis()); } catch(InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { final String[] carName= {"大眾","賓士","寶馬","北京現代","哈佛"}; ExecutorService executorService = newCachedThreadPool();//執行緒池 for(int i=0;i<carName.length;i++) { partCarRunable carRunable = new partCarRunable(carName[i]); executorService.execute(carRunable); } } }

結果

執行緒名稱:pool-1-thread-1,車型別:大眾,當前可用停車位:0,當前時間為:1542180562936
大眾已離場,當前可用停車位:1,當前時間為:1542180562937
執行緒名稱:pool-1-thread-2,車型別:賓士,當前可用停車位:0,當前時間為:1542180562937
賓士已離場,當前可用停車位:1,當前時間為:1542180562938
執行緒名稱:pool-1-thread-3,車型別:寶馬,當前可用停車位:0,當前時間為:1542180562938
寶馬已離場,當前可用停車位:1,當前時間為:1542180562938
執行緒名稱:pool-1-thread-4,車型別:北京現代,當前可用停車位:0,當前時間為:1542180562938
北京現代已離場,當前可用停車位:1,當前時間為:1542180562938
執行緒名稱:pool-1-thread-5,車型別:哈佛,當前可用停車位:0,當前時間為:1542180562939
哈佛已離場,當前可用停車位:1,當前時間為:1542180562939

說明當訊號量為1時候,則同一時刻只佔用一個停車位,只能等它釋放之後才能進來後續的車輛進場,如果當訊號量沒釋放,則後面的車輛進場將一直處於等待狀態.

2.如果訊號量為2的非公平性訊號量時,即
private static final Semaphore semaphore=new Semaphore(2);

則看結果

執行緒名稱:pool-1-thread-1,車型別:大眾,當前可用停車位:1,當前時間為:1542181505223
離開:pool-1-thread-1,大眾已離場,當前可用停車位:2,當前時間為:1542181505224
執行緒名稱:pool-1-thread-2,車型別:賓士,當前可用停車位:1,當前時間為:1542181505225
執行緒名稱:pool-1-thread-3,車型別:寶馬,當前可用停車位:0,當前時間為:1542181505225
離開:pool-1-thread-3,寶馬已離場,當前可用停車位:1,當前時間為:1542181505225
離開:pool-1-thread-2,賓士已離場,當前可用停車位:2,當前時間為:1542181505225
執行緒名稱:pool-1-thread-4,車型別:北京現代,當前可用停車位:1,當前時間為:1542181505226
離開:pool-1-thread-4,北京現代已離場,當前可用停車位:2,當前時間為:1542181505226
執行緒名稱:pool-1-thread-5,車型別:哈佛,當前可用停車位:1,當前時間為:1542181505229
離開:pool-1-thread-5,哈佛已離場,當前可用停車位:2,當前時間為:1542181505229

這是非公平訊號量,通過結果,可以看出有兩個空餘車位時候,同一時刻有兩個車輛執行緒同時進入車庫停車,剛開始執行緒1進入後馬上又離開,此時剩餘2個車位,後執行緒2,3車輛進場,此時已無空餘車位,執行緒等待,後執行緒3,執行緒2離場,以此類推,非公平性訊號量可以看出進入和出去的執行緒沒有先後順序,有競爭的情況

3.如果訊號量為2的公平性訊號量時,即
private static final Semaphore semaphore=new Semaphore(2,true);

結果:

執行緒名稱:pool-1-thread-1,車型別:大眾,當前可用停車位:1,當前時間為:1542181386772
離開:pool-1-thread-1,大眾已離場,當前可用停車位:2,當前時間為:1542181386772
執行緒名稱:pool-1-thread-2,車型別:賓士,當前可用停車位:1,當前時間為:1542181386774
離開:pool-1-thread-2,賓士已離場,當前可用停車位:2,當前時間為:1542181386774
執行緒名稱:pool-1-thread-3,車型別:寶馬,當前可用停車位:1,當前時間為:1542181386774
離開:pool-1-thread-3,寶馬已離場,當前可用停車位:1,當前時間為:1542181386774
執行緒名稱:pool-1-thread-4,車型別:北京現代,當前可用停車位:0,當前時間為:1542181386774
離開:pool-1-thread-4,北京現代已離場,當前可用停車位:2,當前時間為:1542181386774
執行緒名稱:pool-1-thread-5,車型別:哈佛,當前可用停車位:1,當前時間為:1542181386774
離開:pool-1-thread-5,哈佛已離場,當前可用停車位:2,當前時間為:1542181386774

通過結果可以看出,當有空餘訊號量時,執行緒進入的順序保持公平性,按順序進場,離場的也按照進場的順序出場.
四.總結
Semaphore主要用於控制當前活動執行緒數目,就如同停車場系統一般,而Semaphore則相當於看守的人,用於控制總共允許停車的停車位的個數,而對於每輛車來說就如同一個執行緒,執行緒需要通過acquire()方法獲取許可,而release()釋放許可。如果許可數達到最大活動數,那麼呼叫acquire()之後,便進入等待佇列,等待已獲得許可的執行緒釋放許可,從而使得多執行緒能夠合理的執行。