CountDownLatch類 和 CyclicBarrier類 詳解與區別

1. CountDownLatch 類

CountDownLatch 類是用於線程同步的工具,作用:讓一個或多個源線程(調用await方法的線程) 必須 等待一個或多個目標線程(調用countDown方法的線程)都執行完成了才能繼續執行自己的代碼。

注意:

1. 不會妨礙目標線程的執行,但是會阻塞源線程,因爲await方法會檢測count是爲0,如果不是0,就會阻塞,不會繼續執行後面的代碼。 

2. 它是一次性的,不能重複用,因爲count變爲0之後,就不會再改變了,只有一個countDown方法去減,沒有方法去讓count加,所以如果你重複使用的話,不起作用,count永遠爲0。

3. 當然,你可以在一個線程的run方法裏調用多次countDown 方法,多減幾個1。

比如一個測評系統,需要測評一個人的優秀值good,good是通過一個計算公式來計算出的,這個公式爲good = 身高 + 體重 + 智力 + 情商 + 顏值,那麼我們可以創建5個線程A,B,C,D,E,分別去測試出:身高、體重、智力、情商、顏值,要想計算出good,必須等待5個線程都執行完畢,身高、體重、智力、情商、顏值都得到了,才能進一步計算good。 此時CountDownLatch類就有用了。

public class CountDownLatch {
    // 構造方法,初始化計數值
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

    //判斷count是否爲0,爲0才能繼續執行下去,否則就一直阻塞
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    //判斷count是否爲0,爲0才能繼續執行下去,否則就一直阻塞,如果阻塞時間超過了unit,就繼續執行
    public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    //讓count減1
    public void countDown() {
        sync.releaseShared(1);
    }

    //獲取count的值
    public long getCount() {
        return sync.getCount();
    }
}

舉個例子:

其實這個例子裏面不應該將CountDownLatch對象定爲靜態的(定義爲普通的就行),因爲不能重複使用,定義爲靜態的幹嘛。

public class Main {
    public static CountDownLatch countDownLatch = new CountDownLatch(2);
    public static void main(String[] args) throws InterruptedException {
        MyThread myThread = new MyThread();
        myThread.start();
        MyThread myThread1 = new MyThread();
        myThread1.start();
        countDownLatch.await();
        System.out.println("徹底結束啦");
    }
}

public class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        System.out.println("當前線程是:" + Thread.currentThread().getName());
        Main.countDownLatch.countDown();
        System.out.println("結束");
    }
}

2. CyclicBarrier類

CyclicBarrier 類也是線程同步工具類,CountDownLatch類能做到的,它都能做到,而且功能更多,作用:僅僅讓多個線程同時開始執行。 形象地比喻爲柵欄,所有達到柵欄的線程都得停下,只能線程數量達到了規定的數量後,纔打開柵欄,讓所有線程同時繼續運行。

注意:

1. 它不會阻塞源線程,但是它阻塞目標線程。

2. 能夠重複使用,就好像count減爲0之後,我們能夠調用reset方法將count再次重置爲初始值。

3. 不用特意調用reset方法去達到循環使用,比如parties是3,直接啓動3個線程後,接着直接啓動別的線程就行。

public class CyclicBarrier {
    //用於記錄同一批線程屬於一代
    private static class Generation {
        boolean broken = false; 
    }
    //入口鎖,就是要達到柵欄,也得一個一個來吧,不能兩個線程同時進行吧,因此要一個鎖
    private final ReentrantLock lock = new ReentrantLock();
    //條件鎖,當線程到達柵欄時,鎖住它,不讓其執行,等線程數量滿足條件後,鎖打開,線程繼續執行
    private final Condition trip = lock.newCondition();
    // 記錄柵欄開啓所需的線程數,以便重置的時候使用
    private final int parties;
    //線程數量達到要求後,執行的內容
    private final Runnable barrierCommand;
    private Generation generation = new Generation();
    //count類似於CountDownLatch中的count
    private int count;
    //進入下一代了,上一批線程已經處理完了,可以接受下一批了
    private void nextGeneration() {
        trip.signalAll();
        count = parties;
        generation = new Generation();
    }
    //終止柵欄
    private void breakBarrier() {
        generation.broken = true;
        count = parties;
        trip.signalAll();
    }

    //await就是通過dowait實現的
    private int dowait(boolean timed, long nanos)
        throws InterruptedException, BrokenBarrierException,
               TimeoutException {
        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();
            }

            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
            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();

                if (g != generation)
                    return index;

                if (timed && nanos <= 0L) {
                    breakBarrier();
                    throw new TimeoutException();
                }
            }
        } finally {
            lock.unlock();
        }
    }

    //構造方法,當柵欄被開啓時,barrierAction由最後一個到達柵欄的線程執行
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

    // 構造方法
    public CyclicBarrier(int parties) {
        this(parties, null);
    }

    // 獲取打開柵欄所需的線程數量,就是我們初始化時候指定的那個int參數
    public int getParties() {
        return parties;
    }

    // 調用這個方法的線程已經就緒等待了
    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

    // 這個方法是目標線程調用的,表示調用此方法的線程已經就緒等待着,
    // 如果等待時間超過unit,就不等了,直接執行自己的
    public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

    // 判斷柵欄是否被破壞
    public boolean isBroken() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return generation.broken;
        } finally {
            lock.unlock();
        }
    }

    // 重置爲初始化狀態,如果此時有線程在等待,線程會拋出異常
    public void reset() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            breakBarrier();   // break the current generation
            nextGeneration(); // start a new generation
        } finally {
            lock.unlock();
        }
    }

    // 獲取當前正等待着的(或者說準備就緒了)線程的數量
    public int getNumberWaiting() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return parties - count;
        } finally {
            lock.unlock();
        }
    }
}

 

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