Java併發編程——AQS組件之CyclicBarrier

一、AQS組件CyclicBarrier

1.CyclicBarrier介紹

  • CyclicBarrierAQS其他組件,也是一個同步輔助類,它允許一組線程相互等待,直到到達某個公共的屏障點(循環屏障)
  • CyclicBarrier可以完成多個線程之間相互等待,只有每個線程都準備就緒後才能繼續往下執行後面的操作。
  • 每當有一個線程執行了await方法,計數器就會執行+1操作,待計數器達到預定的值,所有的線程再同時繼續執行。由於計數器釋放之後可以重用(reset方法),所以稱之爲循環屏障。

2.CyclicBarrierCountDownLatch的區別

  • CountDownLatch只有一個計數器,且不可重複使用;CyclicBarrier的計數器可以重複使用;
  • CountDownLatch描述的是一個或多個線程等待其他線程的關係;CyclicBarrier描述的是多個線程相互等待的關係,直到所有線程都滿足了條件才繼續執行後續的操作;

二、CyclicBarrier的應用

1. await()

允許一組線程相互等待,直到到達某個公共的屏障點。

await()方法源碼:

    public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
    }

情景:

  線程池中10個,每次線程執行了輸出ready日誌操作後,進入等待狀態,直到有5個線程都執行了輸出ready日誌操作,才允許執行後續輸出contunie日誌操作,循環往復。示例代碼如下:

@Slf4j
public class CyclicBarrierExample1 {

    //實例化一個CyclicBarrier,並初始化屏障數爲5;
    private static CyclicBarrier barrier = new CyclicBarrier(5);

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i=0; i<10; i++){
            final int threadNum = i;
            //此處爲了便於觀察,讓每個線程間隔1秒
            Thread.sleep(1000);
            executorService.execute(()->{
                try {
                    race(threadNum);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }

    private static void race(int threadNum) throws Exception{
        Thread.sleep(1000);
        log.info("{} is ready",threadNum);
        //線程進入等待的狀態,直至等待線程數滿足CyclicBarrier 初始化的5
        barrier.await();
        log.info("{} is continue",threadNum);
    }
}

執行結果如下:

10:03:32.886 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 0 is ready
10:03:33.801 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 1 is ready
10:03:34.778 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 2 is ready
10:03:35.778 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 3 is ready
10:03:36.778 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 4 is ready
10:03:36.778 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 4 is continue
10:03:36.778 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 3 is continue
10:03:36.778 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 2 is continue
10:03:36.778 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 1 is continue
10:03:36.778 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 0 is continue
10:03:37.780 [pool-1-thread-6] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 5 is ready
10:03:38.780 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 6 is ready
10:03:39.780 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 7 is ready
10:03:40.780 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 8 is ready
10:03:41.780 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 9 is ready
10:03:41.780 [pool-1-thread-6] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 5 is continue
10:03:41.780 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 6 is continue
10:03:41.780 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 9 is continue
10:03:41.780 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 8 is continue
10:03:41.780 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample1 - 7 is continue

Process finished with exit code 1

執行的結果與預期的一致。

2. await(long timeout, TimeUnit unit)

設置超時時間,若該等待超過了設定值,該線程將不會繼續等待,而是繼續執行

await(long timeout, TimeUnit unit)方法源碼如下:

    public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

BrokenBarrierException解讀:已經進入等待狀態的線程,由於等待時間達到了超時時間,該線程將不再繼續等待,因此CyclicBarrier維護的線程等待隊列的狀態突然發生了改變,所有會拋出異常;

情景:

  線程池中10個,每次線程執行了輸出ready日誌操作後,進入等待狀態,直到有5個線程都執行了輸出ready日誌操作,才允許執行後續輸出contunie日誌操作,但是如果等待時間超過了2秒,則該線程將不必等待,直接繼續執行。循環往復。示例代碼如下:

@Slf4j
public class CyclicBarrierExample2 {

    //實例化一個CyclicBarrier,並初始化屏障數爲5;
    private static CyclicBarrier barrier = new CyclicBarrier(5);

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i=0; i<10; i++){
            final int threadNum = i;
            Thread.sleep(1000);
            executorService.execute(()->{
                try {
                    race(threadNum);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }

    private static void race(int threadNum) throws Exception{
        Thread.sleep(1000);
        log.info("{} is ready",threadNum);
        try {
            //設置超時時間,若該等待超過了設定值,該線程將不會繼續等待,而是繼續執行
            barrier.await(2000, TimeUnit.MILLISECONDS);
        }catch (Exception e){
            log.warn("Exception:{}" ,e.getMessage());
        }

        log.info("{} is continue",threadNum);
    }
}

執行結果如下:

10:37:57.310 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 0 is ready
10:37:58.310 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 1 is ready
10:37:59.309 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 2 is ready
10:37:59.319 [pool-1-thread-1] WARN com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - Exception:null
10:37:59.320 [pool-1-thread-2] WARN com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - Exception:null
10:37:59.320 [pool-1-thread-3] WARN com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - Exception:null
10:37:59.321 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 1 is continue
10:37:59.321 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 2 is continue
10:37:59.320 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 0 is continue
10:38:00.310 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 3 is ready
10:38:00.311 [pool-1-thread-4] WARN com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - Exception:null
10:38:00.311 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 3 is continue
10:38:01.310 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 4 is ready
10:38:01.310 [pool-1-thread-1] WARN com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - Exception:null
10:38:01.310 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 4 is continue
10:38:02.310 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 5 is ready
10:38:02.310 [pool-1-thread-4] WARN com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - Exception:null
10:38:02.310 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 5 is continue
10:38:03.311 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 6 is ready
10:38:03.311 [pool-1-thread-1] WARN com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - Exception:null
10:38:03.311 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 6 is continue
10:38:04.311 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 7 is ready
10:38:04.311 [pool-1-thread-1] WARN com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - Exception:null
10:38:04.311 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 7 is continue
10:38:05.311 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 8 is ready
10:38:05.311 [pool-1-thread-4] WARN com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - Exception:null
10:38:05.311 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 8 is continue
10:38:06.312 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 9 is ready
10:38:06.312 [pool-1-thread-1] WARN com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - Exception:null
10:38:06.312 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample2 - 9 is continue

Process finished with exit code 1

3.new CyclicBarrier(int parties, Runnable barrierAction);

CyclicBarrier在實例化的時候,可以設置一個優先線程。當線程等待數達到屏障值,則優先執行此優先線程;

CyclicBarrier(int parties, Runnable barrierAction)構造方法源碼

    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

情景:

  線程池中10個,每次線程執行了輸出ready日誌操作後,進入等待狀態。當有5個線程都執行了輸出ready日誌操作,首先執行優先線程來打印“當線程等待數達到設定值,優先執行此線程”內容,然後所有等待的線程才允許執行後續輸出contunie日誌操作,循環往復。示例代碼如下:

@Slf4j
public class CyclicBarrierExample3 {

    //實例化一個CyclicBarrier,並初始化屏障數爲5;
    private static CyclicBarrier barrier = new CyclicBarrier(5,()->{
        log.info("當線程等待數達到設定值,優先執行此線程");
    });

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();

        for (int i=0; i<10; i++){
            final int threadNum = i;
            Thread.sleep(1000);
            executorService.execute(()->{
                try {
                    race(threadNum);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }

    private static void race(int threadNum) throws Exception{
        Thread.sleep(1000);
        log.info("{} is ready",threadNum);
        barrier.await();
        log.info("{} is continue",threadNum);
    }
}

輸出結果如下:

10:49:19.972 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 0 is ready
10:49:20.968 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 1 is ready
10:49:21.973 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 2 is ready
10:49:23.001 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 3 is ready
10:49:23.971 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 4 is ready
10:49:23.971 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 當線程等待數達到設定值,優先執行此線程
10:49:23.971 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 4 is continue
10:49:23.971 [pool-1-thread-1] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 0 is continue
10:49:23.972 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 3 is continue
10:49:23.971 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 1 is continue
10:49:23.972 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 2 is continue
10:49:24.972 [pool-1-thread-6] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 5 is ready
10:49:25.972 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 6 is ready
10:49:26.972 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 7 is ready
10:49:27.973 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 8 is ready
10:49:28.973 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 9 is ready
10:49:28.973 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 當線程等待數達到設定值,優先執行此線程
10:49:28.973 [pool-1-thread-4] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 9 is continue
10:49:28.973 [pool-1-thread-2] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 7 is continue
10:49:28.973 [pool-1-thread-6] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 5 is continue
10:49:28.973 [pool-1-thread-3] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 6 is continue
10:49:28.973 [pool-1-thread-5] INFO com.mmall.concurrency.example.aqs.CyclicBarrierExample3 - 8 is continue

Process finished with exit code 0

結果與預期的一致。

Java併發編程學習系列

如有幫助,煩請點贊收藏一下啦 (◕ᴗ◕✿)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章