學過操作系統的朋友都知道信號量,在java.util.concurrent包中也有一個關于信號量的實現:Semaphore。
從代碼實現的角度來說,信號量與鎖很類似,可以看成是一個有限的共享鎖,即只能被有限數量的線程使用的共享鎖。
因為存在計數,因此Semaphore的構造函數有參數permits來設定計數:
public Semaphore(int permits) { sync = new NonfairSync(permits); }
涉及到線程排隊等待的問題,Semaphore也支持fair和unfair模式:
public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits); }
說到線程排隊,前面在說“鎖”的時候講過AbstractQueuedSynchronizer,它實現了類似獲取鎖失敗,管理等待的線程的功能。因此信號量的實現同樣需要借助這個類。
abstract static class Sync extends AbstractQueuedSynchronizer// Unfair模式的實現static final class NonfairSync extends Sync// Fair模式的實現static final class FairSync extends Sync
Sync類使用AbstractQueuedSynchronizer的state來存儲信號量的計數:
Sync(int permits) { setState(permits); }
因為信號量與共享鎖類似,因此在獲取資源和釋放資源的時候使用的都是AbstractQueuedSynchronizer的shared類型的方法。
再次回到前面的unfair和fair模式,這種所謂的公平體現在獲取鎖的時候:unfair是后來先得,fair是先來先得。來看兩者的嘗試獲取資源的方法:
// unfair模式 final int nonfairTryAcquireShared(int acquires) { // 直接檢查是不是有資源,根本不看前面有沒有其他排隊的 for (;;) { int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } } // fair模式 PRotected int tryAcquireShared(int acquires) { for (;;) { // 先看看有沒有排隊的 if (hasQueuedPredecessors()) return -1; int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } }
對于信號量來說,獲取資源的過程,就是一個更新資源計數的過程。對于釋放資源來說,也是一樣。
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"); if (compareAndSetState(current, next)) return true; } }
關于信號量的實現,有了AbstractQueuedSynchronizer和鎖的基礎,是非常好理解的。
新聞熱點
疑難解答