CyclicBarrier 公共屏障点
- 博客分类:
- 多线程
在实际应用中,有时候需要多个线程同时工作以完成同一件事情,而且在完成过程中,往往会等待其他线程都完成某一阶段后再执行,等所有线程都到达某一个阶段后再统一执行。
JDK:
一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。
CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。
对于失败的同步尝试,CyclicBarrier 使用了一种快速失败的、要么全部要么全不 (all-or-none) 的破坏模式:如果因为中断、失败或者超时等原因,导致线程过早地离开了屏障点,那么其他所有线程(甚至是那些尚未从以前的 await() 中恢复的线程)也将通过 BrokenBarrierException(如果它们几乎同时被中断,则用 InterruptedException)以反常的方式离开。
JDK地址:
- //构造方法摘要
- CyclicBarrier(int parties)
- //创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在每个 barrier 上执行预定义的操作。
- CyclicBarrier(int parties, Runnable barrierAction)
- //创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。
Example1:
- package tags;
- import java.util.Random;
- import java.util.concurrent.CyclicBarrier;
- /**
- * CyclicBarrier调用CyclicBarrier.await()进入等待的线程数,
- * 当线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续。
- * CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。
- * CyclicBarrier初始时还可带一个Runnable的参数,此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。
- */
- public class CyclicBarrierTest {
- public static class ComponentThread implements Runnable {
- CyclicBarrier barrier;// 计数器
- int ID; // 组件标识
- int[] array; // 数据数组
- // 构造方法
- public ComponentThread(CyclicBarrier barrier, int[] array, int ID) {
- this.barrier = barrier;
- this.ID = ID;
- this.array = array;
- }
- public void run() {
- try {
- array[ID] = new Random().nextInt(100);
- System.out.println("Component " + ID + " generates: " + array[ID]);
- System.out.println("Component " + ID + " sleep"); // 在这里等待Barrier处
- barrier.await();
- System.out.println("Component " + ID + " awaked");
- // 计算数据数组中的当前值和后续值
- int result = array[ID] + array[ID + 1];
- System.out.println("Component " + ID + " result: " + result);
- } catch (Exception ex) {
- }
- }
- }
- /**
- * 测试CyclicBarrier的用法
- */
- public static void testCyclicBarrier() {
- final int[] array = new int[3];
- CyclicBarrier barrier = new CyclicBarrier(2, new Runnable() {
- // 在所有线程都到达Barrier时执行
- public void run() {
- System.out.println("testCyclicBarrier run");
- array[2] = array[0] + array[1];
- }
- });
- // 启动线程
- new Thread(new ComponentThread(barrier, array, 0)).start();
- new Thread(new ComponentThread(barrier, array, 1)).start();
- }
- public static void main(String[] args) {
- CyclicBarrierTest.testCyclicBarrier();
- }
- }
结果:
Component 1 generates: 40
Component 0 generates: 8
Component 1 sleep
Component 0 sleep
testCyclicBarrier run
Component 0 awaked
Component 0 result: 48
Component 1 awaked
Component 1 result: 88
两个线程分别执行,互不影响 ,执行到barrier.await();时该线程进入等待状态,
当两个线程都执行到barrier.await();时,达到CyclicBarrier启动所需的阻塞线程数,进入到new CyclicBarrier(2, new Runnable()...)里面的方法, 执行完里面的方法后,等待的两个线程再次被唤醒,继续各自执行线程后面的语句。
Example2:
比如有几个旅行团需要途经深圳、广州、韶关、长沙最后到达武汉。旅行团中有自驾游的,有徒步的,有乘坐旅游大巴的;这些旅行团同时出发,并且每到一个目的地,都要等待其他旅行团到达此地后再同时出发,直到都到达终点站武汉。
这时候CyclicBarrier就可以派上用场。CyclicBarrier最重要的属性就是参与者个数,另外最要方法是await()。当所有线程都调用了await()后,就表示这些线程都可以继续执行,否则就会等待。
- package tags;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import java.util.concurrent.BrokenBarrierException;
- import java.util.concurrent.CyclicBarrier;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- public class TestCyclicBarrier {
- // 徒步需要的时间: Shenzhen, Guangzhou, Shaoguan
- private static int[] timeWalk = { 5, 8, 15};
- // 自驾游
- private static int[] timeSelf = { 1, 3, 4};
- // 旅游大巴
- private static int[] timeBus = { 2, 4, 6};
- static String now() {
- SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
- return sdf.format(new Date()) + ": ";
- }
- static class Tour implements Runnable {
- private int[] times;
- private CyclicBarrier barrier;
- private String tourName;
- public Tour(CyclicBarrier barrier, String tourName, int[] times) {
- this.times = times;
- this.tourName = tourName;
- this.barrier = barrier;
- }
- public void run() {
- try {
- Thread.sleep(times[0] * 1000); //使用 times岔开各组时间,使其显示不同的now()
- System.out.println(now() + tourName + " Reached Shenzhen");
- barrier.await();
- Thread.sleep(times[1] * 1000);
- System.out.println(now() + tourName + " Reached Guangzhou");
- barrier.await();
- Thread.sleep(times[2] * 1000);
- System.out.println(now() + tourName + " Reached Shaoguan");
- } catch (InterruptedException e) {
- } catch (BrokenBarrierException e) {
- }
- }
- }
- public static void main(String[] args) {
- // 三个旅行团
- CyclicBarrier barrier = new CyclicBarrier(3);
- ExecutorService exec = Executors.newFixedThreadPool(3);
- exec.submit(new Tour(barrier, "WalkTour", timeWalk));
- exec.submit(new Tour(barrier, "SelfTour", timeSelf));
- exec.submit(new Tour(barrier, "BusTour", timeBus));
- exec.shutdown();
- }
- }
结果:
15:13:11: SelfTour Reached Shenzhen
15:13:12: BusTour Reached Shenzhen
15:13:15: WalkTour Reached Shenzhen
15:13:18: SelfTour Reached Guangzhou
15:13:19: BusTour Reached Guangzhou
15:13:23: WalkTour Reached Guangzhou
15:13:27: SelfTour Reached Shaoguan
15:13:29: BusTour Reached Shaoguan
15:13:38: WalkTour Reached Shaoguan