Semaphore-訊號量的實現分析

斷風雨發表於2019-02-26

Semaphore

Semaphore 訊號量:可以用來控制同時訪問特定資源的執行緒數量;通過協調各個執行緒以保證合理的使用公共資源。

構造

// permits 設定許可證的數量
public Semaphore(int permits) {
    // 預設非公平
    sync = new NonfairSync(permits);
}
// permits 設定許可數量
// fair 設定是否採用公平模式
public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
複製程式碼

非公平模式

acquire()

獲取許可。只有當獲取到可用的許可,或者當前執行緒被中斷;否則該執行緒被阻塞

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
複製程式碼

預設會呼叫 NonfairSync 下的 tryAcquireShared 方法,繼續呼叫父類的 nonfairTryAcquireShared 方法

protected int tryAcquireShared(int acquires) {
    return nonfairTryAcquireShared(acquires);
}
複製程式碼
final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        // 獲取當前 state 值
        // 也即是當前可用的許可數
        int available = getState();
        // 當前可用許可數減去嘗試獲取許可的數
        // 得到剩餘許可數
        int remaining = available - acquires;
        // remaining < 0 說明當前可用許可數小於嘗試獲取許可數,也即是獲取同步狀態失敗 直接返回 remaining, 退出迴圈 當前執行緒會被新增到同步佇列中
        // remaining > 0 說明當前可用許可數大於嘗試獲取許可數,
        // 則執行 compareAndSetState 更新 state , 若更新成功則返回 退出迴圈 當前執行緒獲取到許可
        // 若 compareAndSetState 更新失敗,說明有其他執行緒獲取到許可,則繼續輪詢
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}
複製程式碼

release

釋放許可

public void release() {
    sync.releaseShared(1);
}
複製程式碼
protected final boolean tryReleaseShared(int releases) {
    for (;;) {
        // 獲取當前許可數
        int current = getState();
        // 當前許可 + 釋放的許可數
        int next = current + releases;
        if (next < current) // overflow
            throw new Error("Maximum permit count exceeded");
        // 更新 state 值, 更新成功則返回 true 退出迴圈;並喚醒同步佇列上阻塞的執行緒
        // 更新 state 值失敗,說明有其他執行緒獲取許可或釋放了許可,則繼續輪詢
        if (compareAndSetState(current, next))
            return true;
    }
}
複製程式碼

公平模式

acquire

公平模式下獲取許可

protected int tryAcquireShared(int acquires) {
    for (;;) {
        // 判斷同步佇列上是否有阻塞的執行緒
        // 若有的話,返回 -1 表示獲取許可失敗 退出迴圈加入同步佇列中
        if (hasQueuedPredecessors())
            return -1;
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}
複製程式碼

從上述程式碼中可以看到,公平模式下獲取許可和非公平模式下基本類似,只是為了保證 FIFO ,新增了 hasQueuedPredecessors 判斷限制。

release

公平模式下與非公平模式一樣

小結

Semaphore 可以用來實現限流的作用。

相關文章