1. 程式人生 > >[Java併發程式設計實戰] 柵欄 CyclicBarrier 實現(含程式碼)

[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),歡迎關注第一時間獲取更新。