1. 程式人生 > 實用技巧 >Concurrent同步工具類02 - CyclicBarrier

Concurrent同步工具類02 - CyclicBarrier

簡介

CyclicBarrier 的字面意思是可迴圈使用(Cyclic)的屏障(Barrier)。它要做的事情是,讓一組執行緒到達一個屏障(也可以叫同步點)時被阻塞,直到最後一個執行緒到達屏障時,屏障才會開門,所有被屏障攔截的執行緒才會繼續幹活。CyclicBarrier預設的構造方法是CyclicBarrier(int parties),其引數表示屏障攔截的執行緒數量,每個執行緒呼叫await方法告訴CyclicBarrier我已經到達了屏障,然後當前執行緒被阻塞。

CyclicBarrier一個同步輔助類,它允許一組執行緒互相等待,直到到達某個公共屏障點 (common barrier point)。在涉及一組固定大小的執行緒的程式中,這些執行緒必須不時地互相等待,此時 CyclicBarrier 很有用。因為該 barrier 在釋放等待執行緒後可以重用,所以稱它為迴圈 的 barrier。

需要所有的子任務都完成時,才執行主任務,這個時候就可以選擇使用CyclicBarrier。

CyclicBarrier的應用場景

CyclicBarrier可以用於多執行緒計算資料,最後合併計算結果的應用場景。需要所有的子任務都完成時,才執行主任務,這個時候就可以選擇使用CyclicBarrier。比如我們用一個Excel儲存了使用者所有銀行流水,每個Sheet儲存一個帳戶近一年的每筆銀行流水,現在需要統計使用者的日均銀行流水,先用多執行緒處理每個sheet裡的銀行流水,都執行完之後,得到每個sheet的日均銀行流水,最後,再用barrierAction用這些執行緒的計算結果,計算出整個Excel的日均銀行流水。

應用舉例:

 1 package com.test.lesson01;
 2 
 3 import java.io.IOException;
 4 import java.util.Random;
 5 import java.util.concurrent.BrokenBarrierException;
 6 import java.util.concurrent.CyclicBarrier;
 7 import java.util.concurrent.ExecutorService;
 8 import java.util.concurrent.Executors;
 9 
10 public
class CyclicBarrierTest1 { 11 12 public static void main(String[] args) throws IOException, InterruptedException { 13 //如果將引數改為4,但是下面只加入了3個選手,這永遠等待下去 14 CyclicBarrier barrier = new CyclicBarrier(3); 15 16 ExecutorService executor = Executors.newFixedThreadPool(3); 17 executor.submit(new Thread(new Runner(barrier, "1號選手"))); 18 executor.submit(new Thread(new Runner(barrier, "2號選手"))); 19 executor.submit(new Thread(new Runner(barrier, "3號選手"))); 20 21 executor.shutdown(); 22 } 23 } 24 25 class Runner implements Runnable { 26 // 一個同步輔助類,它允許一組執行緒互相等待,直到到達某個公共屏障點 27 private CyclicBarrier barrier; 28 29 private String name; 30 31 public Runner(CyclicBarrier barrier, String name) { 32 super(); 33 this.barrier = barrier; 34 this.name = name; 35 } 36 37 @Override 38 public void run() { 39 try { 40 Thread.sleep(1000 * (new Random()).nextInt(8)); 41 System.out.println(name + " 準備好了..."); 42 // barrier的await方法,在所有參與者都已經在此 barrier 上呼叫 await 方法之前,將一直等待。 43 barrier.await(); 44 //設定等待時間,如果等待了1秒,最後一個執行緒還沒有就位,則自己繼續執行,但是會導致Barrier被標記為一個已經破壞的Barrier 45 //barrier.await(1,TimeUnit.SECONDS); 46 } catch (InterruptedException e) { 47 System.out.println(name + " 中斷異常!"); 48 } catch (BrokenBarrierException e) { 49 System.out.println(name + " Barrier損壞異常!"); 50 } 51 System.out.println(name + " 起跑!"); 52 } 53 }
View Code

其中的一種執行結果為:(但是起跑一定在準備好了之後)

1 2號選手 準備好了...
2 3號選手 準備好了...
3 1號選手 準備好了...
4 1號選手 起跑!
5 2號選手 起跑!
6 3號選手 起跑!
View Code

CyclicBarrier與CountDownLatch的區別

至此我們難免會將CyclicBarrier與CountDownLatch進行一番比較。這兩個類都可以實現一組執行緒在到達某個條件之前進行等待,它們內部都有一個計數器,當計數器的值不斷的減為0的時候所有阻塞的執行緒將會被喚醒。

有區別的是CyclicBarrier的計數器由自己控制,而CountDownLatch的計數器則由使用者來控制,在CyclicBarrier中執行緒呼叫await方法不僅會將自己阻塞還會將計數器減1,而在CountDownLatch中執行緒呼叫await方法只是將自己阻塞而不會減少計數器的值。

另外,CountDownLatch只能攔截一輪,而CyclicBarrier可以實現迴圈攔截。一般來說用CyclicBarrier可以實現CountDownLatch的功能,而反之則不能,例如上面的賽馬程式就只能使用CyclicBarrier來實現。

參考文獻:

http://ifeve.com/concurrency-cyclicbarrier/#more-14746

https://blog.csdn.net/qq_39241239/article/details/87030142