Semaphore詳解

1、方法

public void acquire()
public void acquireUninterruptibly()
public void release() 
public boolean tryAcquire(int permits)

2、原理
Semaphore原理比較簡單,類似ReentrantLock,使用AQS的模板模式來進行實現。

//Semaphore.java
abstract static class Sync extends AbstractQueuedSynchronizer。。。

Sync有兩種子類,一個公平,一個非公平。

Semaphore構建通常使用如下構造方法,默認是使用非公平模式。

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
     sync = fair ? new FairSync(permits) : new NonfairSync(permits);
 }

2-1、acquire
還是藉助AQS的雙向等待隊列來實現。

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg)
       throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
        //拿state,失敗進入核心等待方法
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

下面來看核心的等待方法。

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    //入隊列
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
        //CAS自旋volatile變量
        for (;;) {
            final Node p = node.predecessor();//前一個節點
            //如果是老二,可以做動作,否則進行park
            if (p == head) {
                //嘗試取state,且state-arg。
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            //在ReentratLock研究過這一段,就是遍歷隊列,然後park
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

2-2、release
release更簡單,就是釋放state,然後隊列下一節點進行unpark即可,由於肯定當前線程執行,都不需要考慮併發。

public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}

先來看tryReleaseShared,就是state+arg

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;
    }
}

再來看doReleaseShared,核心就是喚醒下一個節點。

private void doReleaseShared() {
   /*
     * Ensure that a release propagates, even if there are other
     * in-progress acquires/releases.  This proceeds in the usual
     * way of trying to unparkSuccessor of head if it needs
     * signal. But if it does not, status is set to PROPAGATE to
     * ensure that upon release, propagation continues.
     * Additionally, we must loop in case a new node is added
     * while we are doing this. Also, unlike other uses of
     * unparkSuccessor, we need to know if CAS to reset status
     * fails, if so rechecking.
     */
    for (;;) {
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            if (ws == Node.SIGNAL) {
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;            // loop to recheck cases
                unparkSuccessor(h);
            }
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                // loop on failed CAS
        }
        if (h == head)                   // loop if head changed
            break;
    }
}

通過ReentrantLock看懂了AQS後,深深感覺Semaphore沒啥東西。。。

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