CountDownLatch和CyclicBarrier區別及詳解

JavaDog發表於2019-03-25

   大家在用多執行緒處理的場景的時候經常會使用到這兩個東西,但是大多數情況下都是用前者CountDownLatch比較多,主要是因為前者比較好理解。那本次就從一些實際出發來詳細解說一下這兩者的區別。

CountDownLatch使用

首先CountDownLatch,CountDownLatch類位於java.util.concurrent包下,利用它可以實現類似計數器的功能。比如有一個任務A,它要等待其他4個任務執行完畢之後才能執行,此時就可以利用CountDownLatch來實現這種功能了。通俗點就是主執行緒在所有子執行緒完成之前(計數器大於0)的情況下一隻都會阻塞在那裡,一隻等待到所有子執行緒都執行完畢以後(計數器等於0),主執行緒才會繼續處理。一般多用於分段計算,最後歸併的場景。看個程式碼片段:

public class AtaDemo { 

    public static void main(String[] args){

        final CountDownLatch latch = new CountDownLatch(10);

        ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<Runnable>(5));
        for(int i=0;i<10;i++){
            TestTask task=new TestTask("task_" + (i+1),latch);
            executor.execute(task);
        }
        try {
            System.out.println("等待10個子執行緒執行完畢...");
            latch.await();
            System.out.println("10個子執行緒已經執行完畢");
            System.out.println("繼續執行主執行緒");
        }catch(Exception e){
            e.printStackTrace();
        }
        executor.shutdown();        
    }
}

class TestTask implements Runnable{

    private String name;

    private CountDownLatch latch;

    public TestTask(String name,CountDownLatch latch){
        this.name=name;
        this.latch=latch;
    }

    @Override
    public void run() {
        System.out.println(name + " start running.");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name + " start end.");
        this.latch.countDown();
    }  
}
複製程式碼
執行結果:
task_1 start running.
task_3 start running.
task_2 start running.
task_4 start running.
等待10個子執行緒執行完畢...
task_5 start running.
task_1 start end.
task_4 start end.
task_6 start running.
task_2 start end.
task_8 start running.
task_3 start end.
task_5 start end.
task_7 start running.
task_10 start running.
task_9 start running.
task_8 start end.
task_6 start end.
task_10 start end.
task_9 start end.
task_7 start end.
10個子執行緒已經執行完畢
繼續執行主執行緒
複製程式碼

這樣通過合理分段,可以將資料扔給10個子執行緒計算,最後計算結果可以由主執行緒將子執行緒的計算結果進行最終計算。

CyclicBarrier使用

字面意思迴環柵欄,通過它可以實現讓一組執行緒等待至某個狀態之後再全部同時執行。叫做迴環是因為當所有等待執行緒都被釋放以後,CyclicBarrier可以被重用。可能許多人就理解這個字面可以重用就到此為止了,而且大部分人應該覺得和上面CountDownLatch作用差不多,也就沒有去深入瞭解,網上很多文章也都錯誤的引導大家,使大家覺得CyclicBarrier和CountDownLatch的區別無非就是一個是一次性的,一個是可重用的。下面通過一個例子來演示。這個例子是模擬一個吃飯的場景,一桌10個人,必須等大家都到齊了才能開飯。具體程式碼範例:

public class AtaDemo { 
    public static void main(String[] args){
        CyclicBarrier barrier  = new CyclicBarrier(10);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 200, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue<Runnable>(5));
        for(int i=0;i<10;i++){
            TestTask task=new TestTask("客人_" + (i+1),barrier,i);
            executor.execute(task);
        }       
    }
}

class TestTask implements Runnable{

    private String name;

    private CyclicBarrier barrier;

    private int index;

    public TestTask(String name,CyclicBarrier barrier,int index){
        this.name=name;
        this.barrier=barrier;
        this.index=index;
    }

    @Override
    public void run() {       
        try {
            Thread.sleep(1000 + this.index*1000);
            System.out.println(name + " 哥已到");
            Thread.sleep(1000 + this.index*2000);
            System.out.println(name + " 哥已就坐,其他哥幾個趕快啊,哥餓啦.");
            this.barrier.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(name + "終於都到啦,哥開吃啦,哈哈。");

    }  
}
複製程式碼
執行結果:
客人_1 哥已到
客人_2 哥已到
客人_1 哥已就坐,其他哥幾個趕快啊,哥餓啦.
客人_3 哥已到
客人_4 哥已到
客人_5 哥已到
客人_2 哥已就坐,其他哥幾個趕快啊,哥餓啦.
客人_6 哥已到
客人_7 哥已到
客人_8 哥已到
客人_3 哥已就坐,其他哥幾個趕快啊,哥餓啦.
客人_9 哥已到
客人_10 哥已到
客人_4 哥已就坐,其他哥幾個趕快啊,哥餓啦.
客人_5 哥已就坐,其他哥幾個趕快啊,哥餓啦.
客人_6 哥已就坐,其他哥幾個趕快啊,哥餓啦.
客人_7 哥已就坐,其他哥幾個趕快啊,哥餓啦.
客人_8 哥已就坐,其他哥幾個趕快啊,哥餓啦.
客人_9 哥已就坐,其他哥幾個趕快啊,哥餓啦.
客人_10 哥已就坐,其他哥幾個趕快啊,哥餓啦.
客人_10終於都到啦,哥開吃啦,哈哈。
客人_1終於都到啦,哥開吃啦,哈哈。
客人_3終於都到啦,哥開吃啦,哈哈。
客人_5終於都到啦,哥開吃啦,哈哈。
客人_8終於都到啦,哥開吃啦,哈哈。
客人_2終於都到啦,哥開吃啦,哈哈。
客人_9終於都到啦,哥開吃啦,哈哈。
客人_7終於都到啦,哥開吃啦,哈哈。
客人_6終於都到啦,哥開吃啦,哈哈。
客人_4終於都到啦,哥開吃啦,哈哈。複製程式碼

CountDownLatch和CyclicBarrier區別及詳解


相關文章