java鎖(8)工具類CountDownLatch、CyclicBarrier、Semaphore詳解

1、CountDownLatch實現(xiàn)

CountDownLatch的實現(xiàn)基于AQS的共享模式,其Sync實現(xiàn)如下:

private static final class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 4982264981922014374L;

    //初始的count值,state中保存此count值
    Sync(int count) {
        setState(count);
    }

    int getCount() {
        return getState();
    }

    //嘗試獲取共享鎖,只有當state為0時,即計數(shù)值為0時才能獲取到共享鎖
    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }

    //嘗試釋放共享鎖,通過CAS的方式將state即count值減一
    protected boolean tryReleaseShared(int releases) {
        // Decrement count; signal when transition to zero
        for (;;) {
            int c = getState();
            if (c == 0)
                return false;
            int nextc = c-1;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }
}

CountDownLatch總體流程分三部:
鎖計數(shù)值初始化:即初始化state值為鎖的count值;
await過程:await時會調用tryAcquireShared()獲取共享鎖,若此時state值為大于0,則會將當前線程節(jié)點插入同步隊列尾部,并阻塞當前線程;
countDown過程:countDown時會tryReleaseShared()無阻塞地減少鎖計數(shù)值,當鎖計數(shù)值減少到0時,就會喚醒同步隊列中阻塞的線程節(jié)點。

2、CyclicBarrier實現(xiàn)

CyclicBarrier是基于ReentrantLock和Condition實現(xiàn)的鎖工具。
實現(xiàn)源碼:

//構造函數(shù),parties為初始化的計數(shù)器,barrierAction為當parties計數(shù)減到0時的回調
public CyclicBarrier(int parties, Runnable barrierAction) {
    if (parties <= 0) throw new IllegalArgumentException();
    //初始計數(shù)
    this.parties = parties;
    //本輪計數(shù)值
    this.count = parties;
    //計數(shù)為0時的回調
    this.barrierCommand = barrierAction;
}
//等待
public int await() throws InterruptedException, BrokenBarrierException {
    try {
        return dowait(false, 0L);
    } catch (TimeoutException toe) {
        throw new Error(toe); // cannot happen
    }
}
private int dowait(boolean timed, long nanos)
    throws InterruptedException, BrokenBarrierException,
           TimeoutException {
    //h獲取鎖
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        //獲取當前分代
        final Generation g = generation;

        //此次分代已被停止?
        if (g.broken)
            throw new BrokenBarrierException();
        //當前線程被終止?    
        if (Thread.interrupted()) {
            breakBarrier();
            throw new InterruptedException();
        }
        
        //分代計數(shù)值減1,若計數(shù)值減到0,則運行回調,并調用nextGeneration()
        //喚醒所有在等待的線程,同時更新分代信息
        int index = --count;
        if (index == 0) {  // tripped
            boolean ranAction = false;
            try {
                final Runnable command = barrierCommand;
                if (command != null)
                    command.run();
                ranAction = true;
                nextGeneration();
                return 0;
            } finally {
                if (!ranAction)
                    breakBarrier();
            }
        }

        // loop until tripped, broken, interrupted, or timed out
        //計數(shù)值未減到0,則需要等待
        for (;;) {
            try {
                //無時間條件等待
                if (!timed)
                    trip.await();
                //設置了超時時間等待    
                else if (nanos > 0L)
                    nanos = trip.awaitNanos(nanos);
            } catch (InterruptedException ie) {
                if (g == generation && ! g.broken) {
                    breakBarrier();
                    throw ie;
                } else {
                    // We're about to finish waiting even if we had not
                    // been interrupted, so this interrupt is deemed to
                    // "belong" to subsequent execution.
                    Thread.currentThread().interrupt();
                }
            }
            //等待的過程中分代已經停止,則拋出異常
            //原可能是某個線程在等待的過程中線程被中斷
            if (g.broken)
                throw new BrokenBarrierException();

            //分代已經更新?說明是被signal信號喚醒的
            if (g != generation)
                return index;
            //等待超時?
            if (timed && nanos <= 0L) {
                breakBarrier();
                throw new TimeoutException();
            }
        }
    } finally {
        lock.unlock();
    }
}

3、Semaphore實現(xiàn)

Semaphore的實現(xiàn)是基于AQS的共享鎖,有公平和非公平兩種模式。
基本的Sync實現(xiàn):

abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 1192457210091910933L;

    //初始的許可數(shù)量,同步鎖的state保存許可的數(shù)量
    Sync(int permits) {
        setState(permits);
    }
    
    //獲取許可數(shù)量
    final int getPermits() {
        return getState();
    }

    //非公平的方式獲取共享鎖
    final int nonfairTryAcquireShared(int acquires) {
        for (;;) {
            int available = getState();
            //可用許可數(shù)減去需求的許可數(shù)
            int remaining = available - acquires;
            
            //許可數(shù)大于0時,CAS獲取許可
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }

    //釋放許可
    protected final boolean tryReleaseShared(int releases) {
        for (;;) {
            int current = getState();
            //許可數(shù)加鎖釋放的許可數(shù)
            int next = current + releases;
            if (next < current) // overflow
                throw new Error("Maximum permit count exceeded");
            //CAS更新許可,直到成功    
            if (compareAndSetState(current, next))
                return true;
        }
    }

    //減少許可
    final void reducePermits(int reductions) {
        for (;;) {
            int current = getState();
            int next = current - reductions;
            if (next > current) // underflow
                throw new Error("Permit count underflow");
            //    
            if (compareAndSetState(current, next))
                return;
        }
    }

    //將許可消耗完
    final int drainPermits() {
        for (;;) {
            int current = getState();
            if (current == 0 || compareAndSetState(current, 0))
                return current;
        }
    }
}

公平的FairSync實現(xiàn)::

static final class FairSync extends Sync {
    private static final long serialVersionUID = 2014338818796000944L;

    FairSync(int permits) {
        super(permits);
    }
    
    //公平方式獲取許可
    protected int tryAcquireShared(int acquires) {
        for (;;) {
            //當前節(jié)點有前驅節(jié)點,則獲取失敗
            if (hasQueuedPredecessors())
                return -1;
            //無前驅節(jié)點時,CAS方式獲取許可    
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        }
    }
}

非公平的Sync實現(xiàn):

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = -2694183684443567898L;

    NonfairSync(int permits) {
        super(permits);
    }
    //直接使用Sync的nonfairTryAcquireShared()實現(xiàn),非公平方式獲取許可
    protected int tryAcquireShared(int acquires) {
        return nonfairTryAcquireShared(acquires);
    }
}

相關對外接口實現(xiàn):

//信號量初始化,permits:初始許可數(shù);fire:是否公平
public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
//獲取許可
public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
//釋放許可
public void release() {
    sync.releaseShared(1);
}

4、特性對比

區(qū)別:
CountDownLatch 使一個線程A或是組線程A等待其它線程執(zhí)行完畢后,線程A或是組線程A才繼續(xù)執(zhí)行。CyclicBarrier:一組線程使用await()指定barrier,所有線程都到達各自的barrier后,再同時執(zhí)行各自barrier下面的代碼。Semaphore:是用來控制同時訪問特定資源的線程數(shù)量,它通過協(xié)調各個線程,以保證合理的使用公共資源。
CountDownLatch是減計數(shù)方式,計數(shù)==0時釋放所有等待的線程;CyclicBarrier是加計數(shù)方式,計數(shù)達到構造方法中參數(shù)指定的值時釋放所有等待的線程。Semaphore,每次semaphore.acquire(),獲取一個資源,每次semaphore.acquire(n),獲取n個資源,當達到semaphore 指定資源數(shù)量時就不能再訪問線程處于阻塞,必須等其它線程釋放資源,semaphore.relase()每次資源一個資源,semaphore.relase(n)每次資源n個資源。
CountDownLatch當計數(shù)到0時,計數(shù)無法被重置;CyclicBarrier計數(shù)達到指定值時,計數(shù)置為0重新開始。
CountDownLatch每次調用countDown()方法計數(shù)減一,調用await()方法只進行阻塞,對計數(shù)沒任何影響;CyclicBarrier只有一個await()方法,調用await()方法計數(shù)加1,若加1后的值不等于構造方法的值,則線程阻塞。
CountDownLatch、CyclikBarrier、Semaphore 都有一個int類型參數(shù)的構造方法。CountDownLatch、CyclikBarrier這個值作為計數(shù)用,達到該次數(shù)即釋放等待的線程,而Semaphore 中所有acquire獲取到的資源達到這個數(shù),會使得其它線程阻塞。

共同:
CountDownLatch與CyclikBarrier兩者的共同點是都具有await()方法,并且執(zhí)行此方法會引起線程的阻塞,達到某種條件才能繼續(xù)執(zhí)行(這種條件也是兩者的不同)。Semaphore,acquire方獲取的資源達到最大數(shù)量時,線程再次acquire獲取資源時,也會使線程處于阻塞狀態(tài)。CountDownLatch與CyclikBarrier兩者的共同點是都具有await()方法,并且執(zhí)行此方法會引起線程的阻塞,達到某種條件才能繼續(xù)執(zhí)行(這種條件也是兩者的不同)。Semaphore,acquire方獲取的資源達到最大數(shù)量時,線程再次acquire獲取資源時,也會使線程處于阻塞狀態(tài)。CountDownLatch、CyclikBarrier、Semaphore 都有一個int類型參數(shù)的構造方法。
CountDownLatch、CyclikBarrier、Semaphore 都有一個int類型參數(shù)的構造方法。

CountdownLatch和CyclicBarrier的區(qū)別:
CountDownLatch簡單的說就是一個線程等待,直到他所等待的其他線程都執(zhí)行完成并且調用countDown()方法發(fā)出通知后,當前線程才可以繼續(xù)執(zhí)行。
CyclicBarrier是所有線程都進行等待,直到所有線程都準備好進入await()方法之后,所有線程同時開始執(zhí)行!
CountDownLatch的計數(shù)器只能使用一次。而CyclicBarrier的計數(shù)器可以使用reset() 方法重置。所以CyclicBarrier能處理更為復雜的業(yè)務場景,比如如果計算發(fā)生錯誤,可以重置計數(shù)器,并讓線程們重新執(zhí)行一次。

Semaphore:
Semaphore翻譯成字面意思為 信號量,Semaphore可以控同時訪問的線程個數(shù),通過 acquire() 獲取一個許可,如果沒有就等待,而 release() 釋放一個許可。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容