1. 程式人生 > >Java併發程式設計之CountDownLatch & CyclicBarrier

Java併發程式設計之CountDownLatch & CyclicBarrier

文章目錄

CountDownLatch

概述

CountDownLatch類位於java.util.concurrent包下,利用它可以實現執行緒間同步等功能。
例如,現在有一個主執行緒和兩個子執行緒,主執行緒需要等待兩個子執行緒執行結束之後才能執行,那麼就可以通過CountDownLatch來實現。

構造器 & 方法

構造器

CountDownLatch只提供了一個構造器

public CountDownLatch(int count) {
	if (count < 0) throw new IllegalArgumentException
("count < 0"); this.sync = new Sync(count); }

其中count指的是需要等待子執行緒執行的個數。例如count為2時,指的是主執行緒需要等待兩個子執行緒執行結束,才能夠繼續執行主執行緒。

方法

CountDownLatch主要有以下幾個重要方法

public void await() throws InterruptedException { };
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
public void
countDown() { };
  • await()方法 : 主執行緒呼叫await()方法,當子執行緒未完全執行結束時,主執行緒阻塞在此處。
  • await(long timeout, TimeUnit unit) : 主執行緒呼叫await(long timeout, TimeUnit unit)方法,當子執行緒未完全執行結束時,主執行緒阻塞在此處。當等待了timeout時間之後,子執行緒還是沒有執行結束,則主執行緒也不再繼續阻塞,而是繼續往下執行。
  • countDown() : 子執行緒呼叫countDown(),當子執行緒執行結束之後,呼叫這個方法,類似於計數器減一的操作,當CountDownLatch值變為0的時候,主執行緒可以繼續往下執行。

例子

此處,我們模擬一個主執行緒等待兩個子執行緒的例子:

程式碼

public class CountDownLatchDemo {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread() {
            @Override
            public void run() {
                System.out.println("子執行緒1 執行");
                System.out.println("子執行緒1 結束");
                try {
                    Thread.sleep(3000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                System.out.println("子執行緒2 執行");
                System.out.println("子執行緒2 結束");
                try {
                    Thread.sleep(3000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                countDownLatch.countDown();
            }
        }.start();
        System.out.println("主執行緒等待子執行緒結束");
        try {
            countDownLatch.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("主執行緒 執行");
        System.out.println("主執行緒 結束");
    }
}

執行結果

子執行緒1 執行
子執行緒1 結束
子執行緒2 執行
子執行緒2 結束
主執行緒等待子執行緒結束
主執行緒 執行
主執行緒 結束

CyclicBarrier

概述

CyclicBarrier類位於java.util.concurrent包下,利用它可以實現執行緒間共同等待某個狀態,當等到這個狀態時,執行緒可以繼續往下執行,當沒有達到這個狀態時,所有執行緒阻塞。
例如,現在有一組執行緒1,2,3,4,它們共享同一個cyclicBarrier例項,只有當所有執行緒全部呼叫了cyclicBarrier的await()方法之後,這些執行緒才能夠繼續執行cyclicBarrier.await()之後的程式碼。

構造器 & 方法

構造器

CyclicBarrier提供了二個構造器

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

其中parties指的是需要parties個執行緒等待CyclicBarrier的狀態。barrierAction指的是CyclicBarrier達到狀態之後執行的任務。

方法

CyclicBarrier主要有以下幾個重要方法

public int await() throws InterruptedException, BrokenBarrierException {};
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException {};
  • await()方法 : 執行緒組內所有需要等待CyclicBarrier的執行緒都需要呼叫await()方法,當執行緒呼叫await()後,執行緒阻塞在此處,直到所有執行緒均呼叫了await()方法。
  • await(long timeout, TimeUnit unit) : 執行緒組內所有需要等待CyclicBarrier的執行緒都需要呼叫await()方法,當執行緒呼叫await()後,執行緒阻塞在此處,直到所有執行緒均呼叫了await()方法。當某一部分執行緒等待了timeout時間後,還是未等到CyclicBarrier達到所需的狀態,則丟擲異常,這部分執行緒繼續往下執行

例子1

此處,我們模擬一組執行緒等待某個狀態的例子:

程式碼

public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        new Thread() {
            @Override
            public void run() {
                System.out.println("執行緒1 執行");
                try {
                    Thread.sleep(5000);
                    System.out.println("執行緒1 中途完成");
                    cyclicBarrier.await();
                    System.out.println("執行緒1 繼續執行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }catch(BrokenBarrierException e){
                    e.printStackTrace();
                }
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                System.out.println("執行緒2 執行");
                try {
                    Thread.sleep(1000);
                    System.out.println("執行緒2 中途完成");
                    cyclicBarrier.await();
                    System.out.println("執行緒2 繼續執行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }catch(BrokenBarrierException e){
                    e.printStackTrace();
                }
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                System.out.println("執行緒3 執行");
                try {
                    Thread.sleep(2000);
                    System.out.println("執行緒3 中途完成");
                    cyclicBarrier.await();
                    System.out.println("執行緒3 繼續執行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }catch(BrokenBarrierException e){
                    e.printStackTrace();
                }
            }
        }.start();

    }
}

執行結果

執行緒1 執行
執行緒2 執行
執行緒3 執行
執行緒2 中途完成
執行緒3 中途完成
執行緒1 中途完成
執行緒1 繼續執行
執行緒2 繼續執行
執行緒3 繼續執行

例子2

此處,我們模擬一組執行緒等待某個狀態,當等到這個狀態,執行預先設定好的任務:

程式碼

public class CyclicBarrierDemo1 {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3,new Runnable() {
            @Override
            public void run() {
                System.out.println("當前執行緒" + Thread.currentThread().getName() + "繼續執行");
            }
        });
        new Thread() {
            @Override
            public void run() {
                System.out.println("執行緒1 執行");
                try {
                    Thread.sleep(5000);
                    System.out.println("執行緒1 中途完成");
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }catch(BrokenBarrierException e){
                    e.printStackTrace();
                }
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                System.out.println("執行緒2 執行");
                try {
                    Thread.sleep(1000);
                    System.out.println("執行緒2 中途完成");
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }catch(BrokenBarrierException e){
                    e.printStackTrace();
                }
            }
        }.start();
        new Thread() {
            @Override
            public void run() {
                System.out.println("執行緒3 執行");
                try {
                    Thread.sleep(6000);
                    System.out.println("執行緒3 中途完成");
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }catch(BrokenBarrierException e){
                    e.printStackTrace();
                }
            }
        }.start();
    }
}

執行結果

執行緒1 執行
執行緒2 執行
執行緒3 執行
執行緒2 中途完成
執行緒1 中途完成
執行緒3 中途完成
當前執行緒Thread-2繼續執行

當三個執行緒都到達barrier狀態後,會從三個執行緒中選擇一個執行緒去執行Runnable任務。
具體是怎麼選的呢,經過測試發現,哪一個執行緒最後呼叫的cyclicBarrier.await()方法,則選取哪個執行緒執行Runnable方法。

總結

相同點

  • CountDownLatch和CyclicBarrier都能夠實現執行緒之間的等待

不同點

  • CountDownLatch一般用於某個執行緒等待若干其他執行緒執行完畢(此處的“某個執行緒”不屬於“若干個其他執行緒”)
  • CyclicBarrier一般用於一組執行緒互相等待至某個狀態,相當於這組執行緒等待它們自身達到某個狀態,而不是其他執行緒組達到某個狀態。
  • CountDownLatch是不能重用的,而CyclicBarrier是可以重用的。(本文沒有對這一點進行總結,讀者有興趣可以自己實驗)