1. 程式人生 > 其它 >允許多個執行緒同時訪問的Semaphore(訊號量)

允許多個執行緒同時訪問的Semaphore(訊號量)

技術標籤:JavaAQSjava併發程式設計多執行緒

synchronized 和 ReentrantLock 都是一次只允許一個執行緒訪問某個資源,Semaphore(訊號量)可以指定多個執行緒同時訪問某個資源。

示例程式碼如下:

/**

*

* @author Snailclimb

* @date 2018年9月30日

* @Description: 需要一次性拿一個許可的情況

*/

public class SemaphoreExample1 {

 // 請求的數量

 private static final int threadCount = 550;

 public static void main(String[] args) throws InterruptedException {

  // 建立一個具有固定執行緒數量的執行緒池物件(如果這裡執行緒池的執行緒數量給太少的話你會發現執行的很

慢)

  ExecutorService threadPool = Executors.newFixedThreadPool(300);

  // 一次只能允許執行的執行緒數量。

  final Semaphore semaphore = new Semaphore(20);

  for (int i = 0; i < threadCount; i++) {

   final int threadnum = i;

   threadPool.execute(() -> {// Lambda 表示式的運用

    try {

     semaphore.acquire();// 獲取一個許可,所以可執行執行緒數量為20/1=20

test(threadnum);

     semaphore.release();// 釋放一個許可

   } catch (InterruptedException e) {

     // TODO Auto-generated catch block

     e.printStackTrace();

   }

  });

 }

  threadPool.shutdown();

  System.out.println("finish");

}

 public static void test(int threadnum) throws InterruptedException {

  Thread.sleep(1000);// 模擬請求的耗時操作

  System.out.println("threadnum:" + threadnum);

  Thread.sleep(1000);// 模擬請求的耗時操作

}

}

執行 acquire 方法阻塞,直到有一個許可證可以獲得然後拿走一個許可證;每個 release 方法增加一個許可證,這可能會釋放一個阻塞的 acquire 方法。然而,其實並沒有實際的許可證這個物件,Semaphore 只是維持了一個可獲得許可證的數量。 Semaphore 經常用於限制獲取某種資源的執行緒數量。

當然一次也可以一次拿取和釋放多個許可,不過一般沒有必要這樣做:

semaphore.acquire(5);// 獲取5個許可,所以可執行執行緒數量為20/5=4

test(threadnum);

semaphore.release(5);// 獲取5個許可,所以可執行執行緒數量為20/5=4

除了 acquire 方法之外,另一個比較常用的與之對應的方法是 tryAcquire 方法,該方法如果獲取不到許可就立即返回 false。

Semaphore 有兩種模式,公平模式和非公平模式。

公平模式: 呼叫 acquire 的順序就是獲取許可證的順序,遵循 FIFO;

非公平模式: 搶佔式的。

Semaphore 對應的兩個構造方法如下:

public Semaphore(int permits) {

    sync = new NonfairSync(permits);

 }

  public Semaphore(int permits, boolean fair) {

    sync = fair ? new FairSync(permits) : new NonfairSync(permits);

 }

這兩個構造方法,都必須提供許可的數量,第二個構造方法可以指定是公平模式還是非公平模式,預設非公平模式。

補充:Semaphore與CountDownLatch一樣,也是共享鎖的一種實現。它預設構造AQS的state為permits。當執行任務的執行緒數量超出permits,那麼多餘的執行緒將會被放入阻塞佇列Park,並自旋判斷state是否大於0。只有當state大於0的時候,阻塞的執行緒才能繼續執行,此時先前執行任務的執行緒繼續執行release方法,release方法使得state的變數會加1,那麼自旋的執行緒便會判斷成功。

如此,每次只有最多不超過permits數量的執行緒能自旋成功,便限制了執行任務執行緒的數量。

以上就是本期更新內容,關注下期更精彩哦!