1. 程式人生 > >CountDownLatch 和 CyclicBarrier 的基本使用

CountDownLatch 和 CyclicBarrier 的基本使用

準備 wait com 個人 效果 nbsp 子線程 開始 stack

  CountDownLatch 和 CyclicBarrier 是並發編程中常用的輔助類,兩者使用上有點類似,但又有不同。

一、CountDownLatch

  CountDownLatch 可是實現類似計數器的功能,比如一個線程 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() {
        @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();
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(); } }
View Code

  執行結果: 

子線程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 的基本使用