
CyclicBarrier 的理解和使用
🔥 CyclicBarrier 的理解
CyclicBarrier 是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环的 barrier 。
举个例子,就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是 CyclicBarrier 。
🔥 CyclicBarrier 的方法说明
🎉 构造方法
CyclicBarrier(int parties):创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待它时,它将跳闸,并且当屏障跳闸时不执行预定义的动作。
CyclicBarrier(int parties, Runnable barrierAction):创建一个新的 CyclicBarrier ,当给定数量的线程(线程)等待时,它将跳闸,当屏障跳闸时执行给定的屏障动作,由最后一个进入屏障的线程执行。
🎉 方法
- int await() 等待所有 parties 已经在这个障碍上调用了 await 。
- int await(long timeout, TimeUnit unit) 等待所有 parties 已经在此屏障上调用 await ,或指定的等待时间过去。
- int getNumberWaiting() 返回目前在屏障处等待的线程个数。
- int getParties() 返回履行这个障碍所需的 parties 数量。
- boolean isBroken() 查询这个障碍是否处于破碎状态。
- void reset() 将屏障重置为初始状态。
🔥 CyclicBarrier 的用法
CyclicBarrier 核心主要有两点:线程组内彼此相互等待,然后大家开始做某件事;这一代结束后开始下一代–重用思想。
CountDownLatch 核心思想为确保某些活动直到其他活动都完成才继续执行,而且 CountDownLatch 不可重用。
举例,班级集体野炊,在大巴上等待所有同学到来才开始出发,一个班级集合完毕出发一辆大巴,这是 CyclicBarrier 。到达目的地后需要同学自行寻找食材,寻找到需要的所有食材后才开始做饭,一个班级做饭活动是一次性的,这是 CountDownLatch 。
🎉 典型用法1
赛跑时,等待所有人都准备好时,才起跑。
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| import lombok.extern.slf4j.Slf4j;
import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService;
import static java.util.concurrent.Executors.*;
@Slf4j public class CyclicBarrierTest {
public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
ExecutorService executorService = newFixedThreadPool(3);
for (int i = 1; i <= 3; i++) { executorService.submit(new Thread(new Runner(cyclicBarrier, i + "号选手"))); } executorService.shutdown(); }
static class Runner implements Runnable {
private CyclicBarrier barrier;
private String name;
Runner(CyclicBarrier barrier, String name) { super(); this.barrier = barrier; this.name = name; }
@Override public void run() { try { Thread.sleep(1000 * (new Random()).nextInt(8)); System.out.println(name + " 准备好了..."); barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } System.out.println(name + " 起跑!"); } } }
|
运行结果:
1 2 3 4 5 6
| 1号选手 准备好了... 3号选手 准备好了... 2号选手 准备好了... 2号选手 起跑! 1号选手 起跑! 3号选手 起跑!
|
🎉 典型用法2
CyclicBarrier 等待与复用:宿舍四哥们约着去操场打球
代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| import java.util.concurrent.*;
public class CyclicBarrierTest1 { private static final ExecutorService executorService = Executors.newFixedThreadPool(4);
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(4, () -> System.out.println("寝室四兄弟一起出发去球场") );
private static class GoThread extends Thread { private final String name;
GoThread(String name) { this.name = name; }
@Override public void run() { System.out.println(name + "开始从宿舍出发"); try { cyclicBarrier.await(); System.out.println(name + "从楼底下出发"); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } }
public static void main(String[] args) { String[] str = {"李明", "王强", "刘凯", "赵杰"}; String[] str1 = {"王二", "洪光", "雷兵", "赵三"}; for (int i = 0; i < 4; i++) { executorService.execute(new GoThread(str[i])); } try { Thread.sleep(4000); System.out.println("四个人一起到达球场,现在开始打球"); System.out.println("现在对CyclicBarrier进行复用....."); System.out.println("又来了一拨人,看看愿不愿意一起打:"); } catch (InterruptedException e) { e.printStackTrace(); } cyclicBarrier.reset(); for (int i = 0; i < 4; i++) { executorService.execute(new GoThread(str1[i])); } try { Thread.sleep(4000); System.out.println("四个人一起到达球场,表示愿意一起打球,现在八个人开始打球"); } catch (InterruptedException e) { e.printStackTrace(); } executorService.shutdown(); } }
|
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| 刘凯开始从宿舍出发 赵杰开始从宿舍出发 王强开始从宿舍出发 李明开始从宿舍出发 寝室四兄弟一起出发去球场 李明从楼底下出发 刘凯从楼底下出发 赵杰从楼底下出发 王强从楼底下出发 四个人一起到达球场,现在开始打球 现在对CyclicBarrier进行复用..... 又来了一拨人,看看愿不愿意一起打: 王二开始从宿舍出发 洪光开始从宿舍出发 雷兵开始从宿舍出发 赵三开始从宿舍出发 寝室四兄弟一起出发去球场 赵三从楼底下出发 王二从楼底下出发 洪光从楼底下出发 雷兵从楼底下出发 四个人一起到达球场,表示愿意一起打球,现在八个人开始打球
|
🔥 CountDownLatch 和 CyclicBarrier 的比较
- CountDownLatch 是线程组之间的等待,即一个(或多个)线程等待N个线程完成某件事情之后再执行;而 CyclicBarrier 则是线程组内的等待,即每个线程相互等待,即N个线程都被拦截之后,然后依次执行。
- CountDownLatch 是减计数方式,而 CyclicBarrier 是加计数方式。
- CountDownLatch 计数为0无法重置,而 CyclicBarrier 计数达到初始值,则可以重置。
- CountDownLatch 不可以复用,而 CyclicBarrier 可以复用。