乐趣区

关于java:突击并发编程JUC系列并发工具-CyclicBarrier

突击并发编程 JUC 系列演示代码地址:
https://github.com/mtcarpenter/JavaTutorial

俗话说趁热要打铁, 上篇中介绍的 CountDownLatch 的根本用法,CountDownLatch 计数器是一次性的,也就是等到计数器值变为 0 后,再调用 CountDownLatchawaitcountdown 办法都会立即返回,这就起不到线程同步的成果了。

对于局部业务须要屡次循环应用,就能够应用本章节的 CyclicBarrierCyclicBarrier的字面意思是可循环应用(Cyclic)的屏障(Barrier),它同样领有 CountDownLatch的性能,CyclicBarrier的字面意思是可循环应用(Cyclic)的屏障(Barrier)。它要做的事件是,让一组线程达到一个屏障(也能够叫同步点)时被阻塞,直到最初一个线程达到屏障时,屏障才会开门,所有被屏障拦挡的线程才会持续运行。

重要办法

  • 结构参数

    • CyclicBarrier(int parties): parties 示意的是参加的线程个数,这个数字通过构造方法进行传递。
    • CyclicBarrier(int parties, Runnable barrierAction): 能够承受一个 Runnable 参数 , 此参数示意栅栏动作,当所有线程达到栅栏后,在所有线程执行下一步动作前,运行参数中的动作,这个动作由最初一个达到栅栏的线程执行。
  • await()

    • await(): 以后线程调用 CyclicBarrier 的该办法时会被阻塞,直到满足上面条件之一才会返回:parties个线程都调用了 await() 办法,也就是线程都到了屏障点;其余线程调用了以后线程的 interrupt() 办法中断了以后线程,则以后线程会抛出 InterruptedException 异样而返回;与以后屏障点关联的 Generation 对象的 broken 标记被设置为 true 时,会抛出 BrokenBarrierException 异样,而后返回。
    • await(long timeout, TimeUnit unit): 以后线程调用 CyclicBarrier 的该办法时会被阻塞,直到满足上面条件之一才会返回:parties个线程都调用了 await() 办法,也就是线程都到了屏障点,这时候返回 true;设置的超时工夫到了后返回false;其余线程调用以后线程的interrupt() 办法中断了以后线程,则以后线程会抛出 InterruptedException 异样而后返回;与以后屏障点关联的 Generation 对象的 broken 标记被设置为 true 时,会抛出 BrokenBarrierException 异样,而后返回。

案例上手

分组期待

跟后面 countDownLatch 一样通过学生的案例进行解说,新日小学的同学全副已在操场上,然而操场的进口的只有三个,进口同时只能包容三个年级,先整顿好的三个年级为一组先出,前面的年级为另一组进行出场,示例如下:

public class CyclicBarrierExample1 {

    private final static int gradeNum = 6;

    private static CyclicBarrier barrier = new CyclicBarrier(3);

    public static void main(String[] args) throws Exception {ExecutorService exec = Executors.newScheduledThreadPool(gradeNum);
        System.out.println("告诉、告诉,请筹备的年级先登程.....");
        for (int i = 0; i < gradeNum; i++) {TimeUnit.SECONDS.sleep(1);
            int gradeName = i + 1;
            exec.submit(() -> {
                try {wait(gradeName);
                } catch (Exception e) {}});
        }
        exec.shutdown();}

    private static void wait(int gradeName) throws Exception {TimeUnit.SECONDS.sleep(1);
        System.out.println(gradeName + "年级所有同学来到了进口......");
        barrier.await();
        System.out.println(gradeName + "年级所有同学到登程");
    }
}

每个子工作在执行完本人的逻辑后会调用 await 办法。一开始计数器值为 3 , 相当于三个班级,当第一个线程调用 await 办法时,计数器值会递加为 1。因为此时计数器值不为 0,所以以后线程就到了屏障点而被阻塞。而后第二个线程调用await 时,会进入屏障,计数器值也会递加,当初计数器值为 0,执行结束后退出屏障点,持续向下运行。

运行后果如下:

告诉、告诉,请筹备的年级先登程.....
1 年级所有同学来到了进口......
2 年级所有同学来到了进口......
3 年级所有同学来到了进口......
3 年级所有同学到登程
1 年级所有同学到登程
2 年级所有同学到登程
4 年级所有同学来到了进口......
5 年级所有同学来到了进口......
6 年级所有同学来到了进口......
6 年级所有同学到登程
5 年级所有同学到登程
4 年级所有同学到登程

超时期待

为了早日达到植树场地,学校领导规定每一个年级从操场进来的工夫为 2 秒,对于超时的引起的异样,再进行异样解决,示例如下

public class CyclicBarrierExample2 {

    private final static int gradeNum = 6;

    private static CyclicBarrier barrier = new CyclicBarrier(3);

    public static void main(String[] args) throws Exception {ExecutorService exec = Executors.newScheduledThreadPool(gradeNum);
        System.out.println("告诉、告诉,请筹备的年级先登程.....");
        for (int i = 0; i < gradeNum; i++) {TimeUnit.SECONDS.sleep(1);
            int gradeName = i + 1;
            exec.submit(() -> {
                try {wait(gradeName);
                } catch (Exception e) {}});
        }
        exec.shutdown();}

    private static void wait(int gradeName) throws Exception {TimeUnit.SECONDS.sleep(1);
        System.out.println(gradeName + "年级所有同学来到了进口......");
        try {barrier.await(2000, TimeUnit.MILLISECONDS);
        } catch (Exception e) {System.out.println("CyclicBarrier 超时异样:" + gradeName + "年级 -" + e);
        }
        System.out.println(gradeName + "年级所有同学到登程");
    }
}

与下面的例子相比,CyclicBarrier 能够设置超时工夫,如 barrier.await(2000, TimeUnit.MILLISECONDS); 子线程超过两秒,就抛出异样,依据本人的业务是中断还是持续向下运行。
运行后果如下:

告诉、告诉,请筹备的年级先登程.....
1 年级所有同学来到了进口......
2 年级所有同学来到了进口......
3 年级所有同学来到了进口......
3 年级所有同学到登程
1 年级所有同学到登程
2 年级所有同学到登程
4 年级所有同学来到了进口......
5 年级所有同学来到了进口......
6 年级所有同学来到了进口......
CyclicBarrier 超时异样:  4 年级 -java.util.concurrent.TimeoutException
4 年级所有同学到登程
CyclicBarrier 超时异样:  5 年级 -java.util.concurrent.BrokenBarrierException
5 年级所有同学到登程
CyclicBarrier 超时异样:  6 年级 -java.util.concurrent.BrokenBarrierException
6 年级所有同学到登程

回调

每一个年级达到入口之后,汇报给领导,领导进行接下来的安顿。示例如下:

public class CyclicBarrierExample3 {

    private final static int gradeNum = 6;

    private static CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
        @Override
        public void run() {System.out.println("****** 所有子线程达到屏障 ******");
        }
    });

    public static void main(String[] args) throws Exception {ExecutorService exec = Executors.newScheduledThreadPool(gradeNum);
        System.out.println("告诉、告诉,请筹备的年级先登程.....");
        for (int i = 0; i < gradeNum; i++) {TimeUnit.SECONDS.sleep(1);
            int gradeName = i + 1;
            exec.submit(() -> {
                try {wait(gradeName);
                } catch (Exception e) {}});
        }
        exec.shutdown();}

    private static void wait(int gradeName) throws Exception {TimeUnit.SECONDS.sleep(1);
        System.out.println(gradeName + "年级所有同学来到了进口......");
        barrier.await();
        System.out.println(gradeName + "年级所有同学到登程");
    }
}

如上代码创立了一个 CyclicBarrier 对象,其第一个参数为计数器初始值,第二个参数 Runable 是当计数器值为 0 是须要执行的工作。当计数器值为 0,这时就会去执行CyclicBarrier 构造函数中的工作,执行结束后退出屏障点,持续向下运行。

CyclicBarrierCountDownLatch 区别

CyclicBarrierCountDownLatch 可能容易混同,咱们强调下它们的区别。

  • CountDownLatch的参加线程是有不同角色的,有的负责倒计时,有的在期待倒计时变为 0,负责倒计时和期待倒计时的线程都能够有多个,用于不同角色线程间的同步。
  • CyclicBarrier的参加线程角色是一样的,用于同一角色线程间的协调一致。
  • CountDownLatch是一次性的,而 CyclicBarrier 是能够反复利用的。

欢送关注公众号 山间木匠, 我是小春哥,从事 Java 后端开发,会一点前端、通过继续输入系列技术文章以文会友,如果本文能为您提供帮忙,欢送大家关注、点赞、分享反对,_咱们下期再见!_

退出移动版