首頁 > 軟體

Java中的Semaphore如何使用

2022-06-27 18:01:21

簡介

semaphore中文意思既是號誌,它的主要功能就是用來控制某個資源同時被存取的執行緒數。

為了控制某塊資源的並行存取量時,可以使用Semaphore物件中的acquire()方法獲取存取令牌,如果Semaphore物件存取令牌已發完,那麼當前獲取令牌的執行緒將會進入阻塞,帶其他執行緒進行release()釋放令牌時,當前執行緒才有機會獲得令牌從而擁有存取許可權。

簡述實現原理

Semaphore實際上是一種共用鎖,因為它允許多個執行緒並行獲取共用的資源。在Semaphore物件建立時必須設定可用令牌的初始數量permits,用於控制並行時同時獲取資源許可權的執行緒數量。在Semaphore類中繼承了同步佇列AbstractQueuedSynchronizer,在此類中有個屬性state用於標記當前並行的佇列數,也就是獲取令牌的執行緒數,那麼在進行acquire()的時候,就會嘗試獲取共用鎖,獲取鎖成功後state值將加1,如果state值已經達到permits時就表示令牌已派發完,當前執行緒將進入阻塞狀態,待其他執行緒進行release()state值將減1,此時就會從佇列中獲取頭部執行緒進行喚醒讓其獲得令牌進行資源存取。

如下圖,仔細檢視原始碼就會發現Semaphore實際上就是重寫了AbstractQueuedSynchronizer中的tryAcquireShared()tryReleaseShared()方法來實現的。

方法介紹

  • Semaphore過載了兩個建構函式,其一是Semaphore(int permits)直接指定令牌數,預設為非公平鎖;其二是Semaphore(int permits,boolean fair),fair引數即表示執行緒搶佔令牌的公平性,true為公平鎖,否則為非公平鎖。
  • acquire()無參表示預設獲取一個令牌,acquire(int permits)表示獲取指定permits數量的令牌數,如果令牌不夠,則當前執行緒進入阻塞狀態。

tryAcquire()無參表示嘗試獲取一個令牌,該方法是非阻塞的,所以如果令牌數不夠獲取失敗返回false,否則就返回true;同時也過載了方法tryAcquire(int permits)指定獲取令牌數,tryAcquire(int permits, long timeout, TimeUnit unit)在有效時間內嘗試獲取指定數量的令牌數,如果超時仍未獲取到令牌則返回false,否則返回true。 release同上支援無參與帶參指定釋放令牌數的方法。 drainPermits()獲取剩下的可用令牌。 hasQueuedThread()用於判斷當前Semaphare範例中是否存在等待獲取令牌的執行緒。

案例分析

分析一下下面這段簡短的程式碼,首先是建立一個號誌為5的Semaphore物件,然後在建立一個執行緒池(這裡為了演示方便,實際開發中不建議使用此執行緒池建立),利用for迴圈並行執行100個執行緒,當執行緒執行時優先獲取一個令牌,線上程中的業務程式碼裡我們做了1秒的休眠,為了展示等待獲取令牌的效果,在延遲1秒執行完業務程式碼時進行令牌釋放,後續的執行緒才能逐個被喚醒獲取令牌存取共用資源。

@Slf4j
public class SemaphoreDemo {

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(5);
        ExecutorService executorService = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            executorService.execute(() -> {
                try {
                    semaphore.acquire();
                    log.info("成功獲取令牌");
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    log.info("釋放令牌");
                    semaphore.release();
                }
            });
        }
        executorService.shutdown();
    }
}
  • 執行結果

由下圖執行結果可見,100個執行緒並行並不是一次性都執行完的,而是要等待前面的執行緒釋放令牌後等待的執行緒才可以獲取令牌進行業務程式碼的執行。

適用場景

Semaphore主要是運用在多執行緒環境中對某一些共用資源的存取量限制,防止多個執行緒並行存取同一資源,可能會導致大多數執行緒獲取資源時都需要進行加鎖,那如果是獲取資料庫中的資料,那麼就可以緩解資料庫的壓力。

另一種情況是用於多執行緒執行的一個流量限制,一般情況下我們可能會通過執行緒池做一步執行緒數的控制,但是某些業務為了減輕CPU的負擔,還是會做一些同時執行執行緒數的限制。

到此這篇關於Java中的Semaphore如何使用的文章就介紹到這了,更多相關Semaphore使用內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com