[Java併發程式設計實戰] 柵欄 CyclicBarrier 實現(含程式碼)
溫故而知新,可以為師矣。—《論語》
它的意思是:“溫習舊知識從而得知新的理解與體會,憑藉這一點就可以成為老師了
PS: 如果覺得本文有用的話,請幫忙點贊,留言評論支援一下哦,您的支援是我最大的動力!謝謝啦~
柵欄(Barrier)類似於閉鎖,他能阻塞一組執行緒直到某個事件發生後再全部同時執行。CyclicBarrier 字面意思是迴環柵欄,迴環的意思是它能夠被重複利用,當然前提是在所有執行緒釋放了以後。
CyclicBarrier 和 CountDownLatch 的主要區別:
- CyclicBarrier 是所有執行緒必須同時到達柵欄位置,才能繼續執行。它用於等待其他執行緒,並且能夠重置使用
- CountDownLatch 用於等待事件,是一次性物件,一旦進入終止狀態,就不能被重置。
- CountDownLatch 通常阻塞的是主執行緒,開鎖以後主執行緒才繼續執行。
- CyclicBarrier 阻塞的是子執行緒,到達柵欄位置後,每個執行緒還可以繼續做自己後續的事情。
CyclicBarrier 的核心方法
首先,看它的建構函式,有兩個建構函式。
//建構函式一
public CyclicBarrier(int parties, Runnable barrierAction) {
if (parties <= 0) throw new IllegalArgumentException();
this .parties = parties;
this.count = parties;
this.barrierCommand = barrierAction;
}
//建構函式二
public CyclicBarrier(int parties) {
this(parties, null);
}
引數 parties 表示參與的執行緒或者任務有多少個。 引數 barrierAction 是在 parties 個執行緒都到達 barrier 狀態後,才會去執行,它是在一個已有的子任務執行緒中執行。
其次,它的主要方法是 await(),同樣也有兩個版本:
public int await() throws InterruptedException, BrokenBarrierException {
try {
return dowait(false, 0L);
} catch (TimeoutException toe) {
throw new Error(toe); // cannot happen
}
}
public int await(long timeout, TimeUnit unit)
throws InterruptedException,
BrokenBarrierException,
TimeoutException {
return dowait(true, unit.toNanos(timeout));
}
第一個方法比較常用,它用來阻塞當前執行緒,直至所有的執行緒到達 barrier 狀態才繼續執行。
第二個方法顧名思義,如果有某個執行緒超時還沒到達 barrier 狀態,將會丟擲 BrokenBarrierException
異常,並且其他已經到達的執行緒繼續執行後續任務。
應用場景
所有的子任務(子執行緒)需要在到達某個狀態前掛起,直到所有的子任務(子執行緒)都到達柵欄狀態時,它們才能繼續執行下一步操作,這個時候就可以選擇使用CyclicBarrier。
來看一個簡單的例子:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest{
private static final int SIZE = 5; //建立子執行緒的數量
public static void main(String[] args) {
//建立關卡物件
CyclicBarrier cb = new CyclicBarrier(SIZE, new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + ":" + " all sub thead end");//所有子執行緒執行完畢時執行
}});
//建立並啟動五個子執行緒
for(int i = 0; i < SIZE; i++) {
new Thread(new SubThread(cb)).start();
}
}
static class SubThread implements Runnable{
private CyclicBarrier cb; //柵欄物件
public SubThread(CyclicBarrier cb) {
this.cb = cb;
}
@Override
public void run() {
//當前執行緒開始執行
System.out.println(Thread.currentThread().getName() + ": sub thread starting running");
try {
cb.await();//計數器加1,等待其他執行緒執行到同樣的地方
//子執行緒全部繼續執行
System.out.println(Thread.currentThread().getName() + ":" +" all sub thread start doing other things");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
執行結果是:
看列印的 log,可以清楚的顯示他們的工作流程了。首先五個執行緒開始執行直接進入阻塞狀態,接著,所有執行緒到達柵欄狀態;然後任意一個執行緒去執行 CyclicBarrier 的 runnable 引數。這個時候,所有子執行緒並沒有釋放,執行完 runnable,五個執行緒將會繼續執行,處理各自的事物。
沒有什麼比自己寫程式碼來驗證更清晰的了。
本文完結,如對你有幫助,歡迎關注我,謝謝啦~
本文原創首發於微信公眾號 [ 林裡少年], id(seaicelin),歡迎關注第一時間獲取更新。