多執行緒與併發----Semaphere同步

pengzhisen123發表於2018-05-13

Semaphore實現訊號燈:  

    Semaphore可以維護當前訪問自身的執行緒個數,並且提供了同步機制。使用Semaphore可以控制同時訪問資源的執行緒個數,例如,實現一個檔案允許的併發訪問數。

 

java.util.concurrent.Semaphore

  一個計數訊號量。從概念上講,訊號量維護了一個許可集。如有必要,在許可可用前會阻塞每一個 acquire(),然後再獲取該許可。每個 release() 新增一個許可,從而可能釋放一個正在阻塞的獲取者。但是,不使用實際的許可物件,Semaphore 只對可用許可的號碼進行計數,並採取相應的行動。 

      Semaphore 通常用於限制可以訪問某些資源(物理或邏輯的)的執行緒數目。例如,下面的類使用訊號量控制對內容池的訪問: 

 class Pool {
   private static final int MAX_AVAILABLE = 100;
   private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
   public Object getItem() throws InterruptedException {
     available.acquire();
     return getNextAvailableItem();
   }
   public void putItem(Object x) {
     if (markAsUnused(x))
       available.release();
   }
   // Not a particularly efficient data structure; just for demo
   protected Object[] items = ... whatever kinds of items being managed
   protected boolean[] used = new boolean[MAX_AVAILABLE];

   protected synchronized Object getNextAvailableItem() {
     for (int i = 0; i < MAX_AVAILABLE; ++i) {
       if (!used[i]) {
          used[i] = true;
          return items[i];
       }
     }
     return null; // not reached
   }
   protected synchronized boolean markAsUnused(Object item) {
     for (int i = 0; i < MAX_AVAILABLE; ++i) {
       if (item == items[i]) {
          if (used[i]) {
            used[i] = false;
            return true;
          } else
            return false;
       }
     }
     return false;
   }
 } 


獲得一項前,每個執行緒必須從訊號量獲取許可,從而保證可以使用該項。該執行緒結束後,將項返回到池中並將許可返回到該訊號量,從而允許其他執行緒獲取該項。注意,呼叫 acquire() 時無法保持同步鎖,因為這會阻止將項返回到池中。訊號量封裝所需的同步,以限制對池的訪問,這同維持該池本身一致性所需的同步是分開的。

構造方法摘要

Semaphore(int permits)           建立具有給定的許可數和非公平的公平設定的 Semaphore

Semaphore(int permits, boolean fair)           建立具有給定的許可數和給定的公平設定的 Semaphore

方法摘要

 void

acquire()           從此訊號量獲取一個許可,在提供一個許可前一直將執行緒阻塞,否則執行緒被中斷

 void

acquire(int permits)           從此訊號量獲取給定數目的許可,在提供這些許可前一直將執行緒阻塞,或者執行緒已被中斷

 void

acquireUninterruptibly()           從此訊號量中獲取許可,在有可用的許可前將其阻塞。

 void

acquireUninterruptibly(int permits)           從此訊號量獲取給定數目的許可,在提供這些許可前一直將執行緒阻塞。

 int

availablePermits()           返回此訊號量中當前可用的許可數。

 int

drainPermits()           獲取並返回立即可用的所有許可。

protected  Collection<Thread>

getQueuedThreads()           返回一個 collection,包含可能等待獲取的執行緒。

 int

getQueueLength()           返回正在等待獲取的執行緒的估計數目。

 boolean

hasQueuedThreads()           查詢是否有執行緒正在等待獲取。

 boolean

isFair()           如果此訊號量的公平設定為 true,則返回 true

protected  void

reducePermits(int reduction)           根據指定的縮減量減小可用許可的數目。

 void

release()           釋放一個許可,將其返回給訊號量。

 void

release(int permits)           釋放給定數目的許可,將其返回到訊號量。

 String

toString()           返回標識此訊號量的字串,以及訊號量的狀態。

 boolean

tryAcquire()          僅在呼叫時此訊號量存在一個可用許可,才從訊號量獲取許可。

 boolean

tryAcquire(int permits)           僅在呼叫時此訊號量中有給定數目的許可時,才從此訊號量中獲取這些許可。

 boolean

tryAcquire(int permits, long timeout, TimeUnit unit)           如果在給定的等待時間內此訊號量有可用的所有許可,並且當前執行緒未被中斷,則從此訊號量獲取給定數目的許可。

 boolean

tryAcquire(long timeout, TimeUnit unit)           如果在給定的等待時間內,此訊號量有可用的許可並且當前執行緒未被中斷,則從此訊號量獲取一個許可。

示例:3個燈 10個人

房間,有多少人都能裝,執行緒數動態變化,來一個人產生一個執行緒

ExecutorService service = Exccutors.newCachedThreadPool();

final Semaphore sp = new Semaphore(3);燈的個數  指定只有3個

//3個燈,來了5個人,有2個人要等,其中有一個辦完事走了,等待的2個哪個先上呢?預設的構造方法不管,誰搶到了誰上。用new Semaphore(3, true)就可以保證先來的先上。

//將燈的個數設定為1就可以達到互斥效果,每次只能有一個執行緒執行

for (int i=0; i<10; i++)來了10個人

{//人的任務  搶燈

Runnable runnable = new Runnable()

{

public void run()

{

sp.acquire();//搶坑了 會拋中斷異常

}//有人佔住燈了,給出提示

SOP(currentThreadName+進入,當前已有(3-sp.availablePermits())個人了)

Thread.sleep(5000)燈辦事

//辦完事打聲招呼

SOP(ThreadName即將離開)

釋放坑的佔有權

sp.release();

SOP(ThreadName已經走了,還有sp.availablePermits()個燈可用)

}

開始任務吧

service.execute(runnable)

}

傳統互斥只能內部釋放鎖this.unlock(),進去this.lock()暈倒了別人就沒法進去了;用訊號燈可以外部釋放,其他執行緒可以釋放再獲取sp.release()   sp.acquire()


相關文章