java.util.concurrent系列之--CyclicBarrier

1、CyclicBarrier用法

cyclic ['saɪklɪk; 'sɪk-] 環的;循環的;週期的java

Barrier ['bærɪə] n. 障礙物,屏障;界線、 vt. 把…關入柵欄 、n. (Barrier)人名;(法)巴里耶ide

字面意思迴環柵欄,經過它能夠實現讓一組線程等待至某個狀態以後再所有同時執行。叫作迴環是由於當所this

有等待線程都被釋放之後,CyclicBarrier能夠被重用。咱們暫且把這個狀態就叫作barrier,當調用await()方線程

法以後,線程就處於barrier了。code

CyclicBarrier類位於java.util.concurrent包下,CyclicBarrier提供2個構造器:圖片

public CyclicBarrier(int parties, Runnable barrierAction) {
}
 
public CyclicBarrier(int parties) {
}

參數parties指讓多少個線程或者任務等待至barrier狀態;參數barrierAction爲當這些線程都達到barrier狀get

態時會執行的內容。it

而後CyclicBarrier中最重要的方法就是await方法,它有2個重載版本:io

public int await() throws InterruptedException, BrokenBarrierException { };

public int await(long timeout, TimeUnit unit)throws 

InterruptedException,BrokenBarrierException,TimeoutException { };

第一個版本比較經常使用,用來掛起當前線程,直至全部線程都到達barrier狀態再同時執行後續任務;for循環

第二個版本是讓這些線程等待至必定的時間,若是還有線程沒有到達barrier狀態就直接讓到達barrier的線程執行後續任務。

2、例子1:

倘若有若干個線程都要進行寫數據操做,而且只有全部線程都完成寫數據操做以後,這些線程才能繼續作後面的事情,此時就能夠利用CyclicBarrier了:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數據...");
            try {
                Thread.sleep(5000);      //以睡眠來模擬寫入數據操做
                System.out.println("線程"+Thread.currentThread().getName()+"寫入數據完畢,等待其餘線程寫入完畢");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("全部線程寫入完畢,繼續處理其餘任務...");
        }
    }
}

執行結果:

輸入圖片說明

從上面輸出結果能夠看出,每一個寫入線程執行完寫數據操做以後,就在等待其餘線程寫入操做完畢。

當全部線程線程寫入操做完畢以後,全部線程就繼續進行後續的操做了。

3、例子2:

若是說想在全部線程寫入操做完以後,進行額外的其餘操做能夠爲CyclicBarrier提供Runnable參數:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N,new Runnable() {
            @Override
            public void run() {
                System.out.println("當前線程"+Thread.currentThread().getName());   
            }
        });
 
        for(int i=0;i<N;i++)
            new Writer(barrier).start();
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數據...");
            try {
                Thread.sleep(5000);      //以睡眠來模擬寫入數據操做
                System.out.println("線程"+Thread.currentThread().getName()+"寫入數據完畢,等待其餘線程寫入完畢");
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println("全部線程寫入完畢,繼續處理其餘任務...");
        }
    }
}

運行結果:

輸入圖片說明

從結果能夠看出,當四個線程都到達barrier狀態後,會從四個線程中選擇一個線程去執行Runnable。

4、例子3:

下面看一下爲await指定時間的效果:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
 
        for(int i=0;i<N;i++) {
            if(i<N-1)
                new Writer(barrier).start();
            else {
                try {
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                new Writer(barrier).start();
            }
        }
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數據...");
            try {
                Thread.sleep(5000);      //以睡眠來模擬寫入數據操做
                System.out.println("線程"+Thread.currentThread().getName()+"寫入數據完畢,等待其餘線程寫入完畢");
                try {
                    cyclicBarrier.await(2000, TimeUnit.MILLISECONDS);
                } catch (TimeoutException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"全部線程寫入完畢,繼續處理其餘任務...");
        }
    }
}

執行結果:

輸入圖片說明

上面的代碼在main方法的for循環中,故意讓最後一個線程啓動延遲,由於在前面三個線程都達到barrier以後,等待了指定的時間發現第四個線程尚未達到barrier,就拋出異常並繼續執行後面的任務。

5、例子4:

另外CyclicBarrier是能夠重用的,看下面這個例子:

public class Test {
    public static void main(String[] args) {
        int N = 4;
        CyclicBarrier barrier  = new CyclicBarrier(N);
 
        for(int i=0;i<N;i++) {
            new Writer(barrier).start();
        }
 
        try {
            Thread.sleep(25000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        System.out.println("CyclicBarrier重用");
 
        for(int i=0;i<N;i++) {
            new Writer(barrier).start();
        }
    }
    static class Writer extends Thread{
        private CyclicBarrier cyclicBarrier;
        public Writer(CyclicBarrier cyclicBarrier) {
            this.cyclicBarrier = cyclicBarrier;
        }
 
        @Override
        public void run() {
            System.out.println("線程"+Thread.currentThread().getName()+"正在寫入數據...");
            try {
                Thread.sleep(5000);      //以睡眠來模擬寫入數據操做
                System.out.println("線程"+Thread.currentThread().getName()+"寫入數據完畢,等待其餘線程寫入完畢");
 
                cyclicBarrier.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }catch(BrokenBarrierException e){
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"全部線程寫入完畢,繼續處理其餘任務...");
        }
    }
}

執行結果:

輸入圖片說明

從執行結果能夠看出,在初次的4個線程越過barrier狀態後,又能夠用來進行新一輪的使用。而CountDownLatch沒法進行重複使用。

相關文章
相關標籤/搜索