[java併發編程]基於信號量semaphore實現限流器

一、什麼是信號量

“信號量”在編程術語中使用單詞semaphore,那什麼是“信號量”?信號量就好比你家廚房入口架子上擺了三把鍋。

  • 如果你的孩子熱奶拿走一把,你的老婆熱湯拿走一把,你的媽媽做菜拿走一把,你想煮麪條就沒有鍋了。當你看到這種情況,你就不會進入廚房了,你處於等待狀態。也就說廚房按照“鍋的數量”作爲信號量,只能容納三個人(線程)。
  • 當你的老婆熱完湯之後,把鍋重新放回架子上,你就可以去獲得一個鍋,你就可以進入廚房了。

二、信號量類Semaphore

通過上文的介紹,我們可以總結出信號量的重要組成部分

  • 計數器:計算信號量的使用情況,鍋(信號)被使用一次減1,鍋(信號)被還回一次加1
  • 等待隊列:當任務數量大於信號量數量上限的時候,任務進入等待隊列

信號量在JDK中是由 java.util.concurrent.Semaphore 實現的,Semaphore提供了兩個構造函數。permits參數代表信號量的數量(鍋的數量),fair代表信號量的獲取是否遵循公平原則。所謂的公平原則就是:先啓動的線程先調用semaphore.acquire();方法,就先得到一個信號“鍋”(permit),遵循先來後到的原則。

public Semaphore(int permits)
public Semaphore(int permits, boolean fair)

常用方法列表

方法名 作用
acquire() 獲取一個permit,在獲取到permit之前,線程處於阻塞狀態
tryAcquire() 嘗試獲取一個permit,獲取成功返回true,否則返回false,不阻塞線程
tryAcquire(long timeout, TimeUnit unit) 和tryAcquire()大部分實現一樣,區別是提供超時設置,在超時時間範圍內多次嘗試,不阻塞線程。
availablePermits() 獲取目前剩餘的信號permit的數量
release() 釋放一個permit,並喚醒一個等待信號permit的線程
hasQueuedThreads() 返回值boolean類型,判斷等待隊列中是否存在等待線程
getQueueLength() 獲取等待隊列中等待線程的數量

三、實現限流器

通過上面的介紹,我相信大家肯定可以想到Semaphore的應用場景。比如:

  • 醫院門診排號器,三個在崗醫生就是3個信號permit,當超出信號量數量的時候,想就診就只能等待
  • 停車場停車功能,n個車位就是n個信號permit,當超出信號量數量的時候,想停車也只能等待

應用場景還有很多很多,大家自己去發會創造力吧。其實無論多少種應用場景說白了:Semaphore實現的就是一個限流器。我們還是以我們家的廚房kitchen裏面的三把鍋wok爲例,實現基於信號量的限流。

public class TestKitchenSemaphore {

  //信號量-3把鍋
  private  static Semaphore threeWoks = new Semaphore(3);

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

    //模擬5個人搶佔3把鍋的場景
    for(int i=0;i < 5;i++){
      Thread.sleep(1000); //模擬進入廚房的先後順序,存在時間間隔
      
      new Thread(() -> {
        try {
          threeWoks.acquire();  //獲取一個permit,信號量計數器減1
          System.out.println(Thread.currentThread().getName()
                  + "拿走了一把鍋,還剩" + threeWoks.availablePermits() + "把鍋");
          Thread.sleep(new Random().nextInt(5000)); //模擬使用鍋的時長

          threeWoks.release();//釋放permit,信號量計數器加1
          System.out.println(Thread.currentThread().getName()
                  + "還回一把鍋,還剩" + threeWoks.availablePermits() + "把鍋");

        } catch (InterruptedException e) {
          e.printStackTrace();
        }
      }).start();

    }
  }
}

上文代碼的輸出如下,我們可以看到每acquire一次信號量減1,每release一次信號量加1。信號量的上限是3,下限是0。當達到上限的時候,只有等先佔據鍋permit的線程釋放,其他線程才能獲取到鍋permit。

Thread-0拿走了一把鍋,還剩2把鍋 
Thread-1拿走了一把鍋,還剩1把鍋
Thread-2拿走了一把鍋,還剩0把鍋               => 備註:5個線程只能獲取3個鍋(上限)
Thread-1還回一把鍋,還剩1把鍋
Thread-3拿走了一把鍋,還剩0把鍋               => 備註:被還回才能被再次佔用,不超過3
Thread-0還回一把鍋,還剩1把鍋
Thread-4拿走了一把鍋,還剩0把鍋               => 備註:被還回才能被再次佔用,不超過3
Thread-2還回一把鍋,還剩1把鍋        
Thread-3還回一把鍋,還剩2把鍋
Thread-4還回一把鍋,還剩3把鍋                  => 備註:用完依次釋放

歡迎關注我的博客,更多精品知識合集

本文轉載註明出處(必須帶連接,不能只轉文字):字母哥博客 - zimug.com

覺得對您有幫助的話,幫我點贊、分享!您的支持是我不竭的創作動力!。另外,筆者最近一段時間輸出瞭如下的精品內容,期待您的關注。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章