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是可以重用的。(本文沒有對這一點進行總結,讀者有興趣可以自己實驗)