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的線程執行後續任務。
倘若有若干個線程都要進行寫數據操做,而且只有全部線程都完成寫數據操做以後,這些線程才能繼續作後面的事情,此時就能夠利用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("全部線程寫入完畢,繼續處理其餘任務..."); } } }
執行結果:
從上面輸出結果能夠看出,每一個寫入線程執行完寫數據操做以後,就在等待其餘線程寫入操做完畢。
當全部線程線程寫入操做完畢以後,全部線程就繼續進行後續的操做了。
若是說想在全部線程寫入操做完以後,進行額外的其餘操做能夠爲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。
下面看一下爲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,就拋出異常並繼續執行後面的任務。
另外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沒法進行重複使用。