CountDownLatch 和 CyclicBarrier 的基本使用
阿新 • • 發佈:2019-01-30
準備 wait com 個人 效果 nbsp 子線程 開始 stack
CountDownLatch 和 CyclicBarrier 是並發編程中常用的輔助類,兩者使用上有點類似,但又有不同。
一、CountDownLatchCountDownLatch 可是實現類似計數器的功能,比如一個線程 A 需要等待其余多個任務執行完畢後才能執行,此時可以使用這個工具類。
構造器:
public CountDownLatch(int count) { }
主要方法:
public void await() throws InterruptedException { }; // 調用await()方法的線程會被掛起,它會等待直到count值為0才繼續執行public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; // 和await()類似,只不過等待一定的時間後count值還沒變為0的話就會繼續執行 public void countDown() { }; // 將count值減1
示例方法:
public static void main(String[] args) { final CountDownLatch latch = new CountDownLatch(2); new Thread() { @OverrideView Codepublic void run() { try { System.out.println("子線程" + Thread.currentThread().getName() + "正在執行"); Thread.sleep(3000); System.out.println("子線程" + Thread.currentThread().getName() + "執行完畢"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start();new Thread() { @Override public void run() { try { System.out.println("子線程" + Thread.currentThread().getName() + "正在執行"); Thread.sleep(3000); System.out.println("子線程" + Thread.currentThread().getName() + "執行完畢"); latch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }.start(); try { System.out.println("等待2個子線程執行完畢..."); latch.await(); System.out.println("2個子線程已經執行完畢"); System.out.println("繼續執行主線程"); } catch (InterruptedException e) { e.printStackTrace(); } }
執行結果:
子線程Thread-0正在執行 等待2個子線程執行完畢... 子線程Thread-1正在執行 子線程Thread-1執行完畢 子線程Thread-0執行完畢 2個子線程已經執行完畢 繼續執行主線程二、CyclicBarrier
CyclicBarrier 可以稱為回環柵,可以實現所有線程同時執行某動作的效果。比如跑步運動員在比賽前需要進行準備工作,等所有運動員都準備完畢後,同時開始比賽。
構造器:
public CyclicBarrier(int parties) { } // 設置線程數量 public CyclicBarrier(int parties, Runnable barrierAction) { } // 設置線程數量,並設置所有線程 await 執行完畢後的方法
主要方法:
public int await() throws InterruptedException, BrokenBarrierException { }; // 掛起線程,直至所有線程達到 barrier 狀態再執行 public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { }; // 同上,掛起線程,等待一定的時間
示例方法:
public class CyclicBarrierTest { public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() { @Override public void run() { System.out.println("所有運動員準備完畢,倒計時3秒後比賽開始...我是裁判:" + Thread.currentThread().getName() + ",當前時間:" + System.currentTimeMillis()); } }); new Competition(barrier, "張三").start(); new Competition(barrier, "李四").start(); new Competition(barrier, "王五").start(); } /** * 跑步比賽內容,所有人準備工作完成後,起跑 */ static class Competition extends Thread { private CyclicBarrier cyclicBarrier; private String name; public Competition(CyclicBarrier cyclicBarrier, String name) { this.cyclicBarrier = cyclicBarrier; this.name = name; } @Override public void run() { int time = new Random().nextInt(10); System.out.println(name + "開始準備工作,預計耗時:" + time + "秒"); try { // 準備中 Thread.sleep(time * 1000); System.out.println(name + "準備完成,等待其他運動員完成"); cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } // 起跑倒計時 try { Thread.sleep(3000); } catch (Exception e) { e.printStackTrace(); } System.out.println(name + "已經起跑...當前時間:" + System.currentTimeMillis()); } } }View Code
執行結果:
張三開始準備工作,預計耗時:5秒 王五開始準備工作,預計耗時:2秒 李四開始準備工作,預計耗時:6秒 王五準備完成,等待其他運動員完成 張三準備完成,等待其他運動員完成 李四準備完成,等待其他運動員完成 所有運動員準備完畢,倒計時3秒後比賽開始...我是裁判:Thread-1,當前時間:1548768695636 張三已經起跑...當前時間:1548768698637 王五已經起跑...當前時間:1548768698637 李四已經起跑...當前時間:1548768698637
執行分析:
三個運動員每個人是一個線程,執行比賽這一個動作。準備工作和起跑是比賽動作的兩個部分,其中準備工作耗時不同,起跑動作又需要所有運動員都準備完畢才可以進行。線程啟動後,每個運動員執行自身的準備工作,然後阻塞等待其余所有線程執行完準備工作(執行完 await 前序動作),再同時執行各自線程的剩余工作(起跑)。
圖1 CyclicBarrier 執行過程分析
1、CountDownLatch 不可重置,無法重用;CyclicBarrier 可以重置,允許重復使用。
2、CountDownLatch 等待對象是一個線程等待多個線程執行完畢後,再自身執行,而 CyclicBarrier 是多個線程之間相互等待,等所有線程執行到統一狀態時,再同時執行後續動作。
CountDownLatch 和 CyclicBarrier 的基本使用