乐趣区

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

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

小伙伴们,大家好,咱们又见面了,突击并发编程 JUC 系列实战并发工具发车了。新的章节还是新的故事解说,不知各位小伙伴们在面试遇到 CountDownLatchCyclicBarrierSemaphoreExchanger工具类灵魂拷问。本章节提供一种并发流程管制的伎俩CountDownLatch

根本介绍

CountDownLatch容许一个或多个线程期待其余线程实现操作。在日常开发中常常会遇到须要在主线程中开启多个线程去并行执行工作,并且主线程须要期待所有子线程执行结束后再进行汇总的场景。在 CountDownLatch 呈现之前个别都应用线程的 join() 办法来实现这一点,然而 join() 办法不够灵便,不可能满足不同场景的须要。应用 JDK 了一系列的并发流程管制伎俩类,会使代码更加优雅。
应用 join 期待一系列操作实现的代码可能如下:

// 一个线程 期待
thread.join();
// 多个线程 期待
for (Thread thread : list) {thread.join();
}

CountdownLatch 运行流程图

如流程图 CountDownLatch 初始值为 3,首先线程会调用 await 办法,以后数量大于 0 线程进行阻塞期待,T1、T2、T3 每一次调用 countDown 办法,计数器缩小 1,计数器为 0 的时候,阻塞的线程 TA 将放行,执行残余操作。

听故事学技术

一年一度的植树节,到来了,新日小学为了响应国家植树绿化的号召,举办了人人参加的”护城小卫士植树流动“,神兽们早早来到了教室,在登程之后每一个班主任都在教室给大家解说植树相干的平安常识,和此次流动目标。所有的神兽期待铃声响起,神兽们他们整齐划一的去操场汇合,如下案例:

public class CountDownLatchExample1 {

    private final static int gradeNum = 6;

    public static void main(String[] args) throws Exception {ExecutorService exec = Executors.newScheduledThreadPool(gradeNum);
        // 铃声信号
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        for (int i = 0; i < gradeNum; i++) {
            int gradeName = i + 1;
            exec.submit(() -> {
                try {countDownLatch.await();
                    wait(gradeName);

                } catch (Exception e) {}});
        }
        // 3 秒之后开启 告诉
        TimeUnit.SECONDS.sleep(3);
        System.out.println("告诉、告诉,请全体同学速来操场汇合.....");
        countDownLatch.countDown();

        exec.shutdown();}

    private static void wait(int gradeName) throws Exception {TimeUnit.MILLISECONDS.sleep((int) Math.random() * 1000);
        System.out.println(gradeName + "年级所有同学达到操场");
    }
}

此段代码六个线程(班级)全副阻塞,主线程期待 3 秒执行 countDownLatch.countDown(),计数器减 1,此时的 countDownLatch 为 0,所有countDownLatch.await() 收到为 0 的信号不再阻塞,继续执行接下来的工作。
运行后果如下:

告诉、告诉,请全体同学速来操场汇合.....
4 年级所有同学达到操场
1 年级所有同学达到操场
6 年级所有同学达到操场
2 年级所有同学达到操场
3 年级所有同学达到操场
5 年级所有同学达到操场

主线程期待子线程实现

领导发话这次流动以年级为单位在操场汇合,每一个年级人数不统一,整顿破费的工夫也不统一,每一个整顿好的班级报道给领导,领导期待六个年级全副整顿实现,对立关上校门登程。通过 CountDownLatch实现案例如下:

public class CountDownLatchExample2 {
    private final static int gradeNum = 6;
    public static void main(String[] args) throws Exception {ExecutorService exec = Executors.newScheduledThreadPool(gradeNum);
        final CountDownLatch countDownLatch = new CountDownLatch(gradeNum);
        for (int i = 0; i < gradeNum; i++) {
            int gradeName = i + 1;
            exec.submit(() -> {
                try {wait(gradeName);
                } catch (Exception e) { } finally {countDownLatch.countDown();
                }
            });
        }
        System.out.println("期待所有年级汇合筹备.....");
      
        System.out.println("所有年级筹备好了,登程.........");
        exec.shutdown();}
    private static void wait(int gradeName) throws Exception {TimeUnit.MILLISECONDS.sleep((int) Math.random() * 1000);
        System.out.println(gradeName + "年级曾经筹备好了");
    }
}

首先咱们通过 newScheduledThreadPool(gradeNum) 创立了六个线程池,每一个线程池代表一个班级,CountDownLatch(gradeNum) 示意咱们须要计数的数量,这里我须要期待六个班级能力执行,TimeUnit.MILLISECONDS.sleep((int) Math.random() * 1000); 随机工夫,用于示意每一个班级整顿的工夫。主线程调用 countDownLatch.await()办法后会被阻塞。子线程执行结束后调用countDownLatch.countDown() 办法让 countDownLatch 外部的计数器减 1,所有子线程执行结束并调用countDown() 办法后计数器会变为 0,这时候主线程的 await()办法才会返回。
运行代码如下:

期待所有年级汇合筹备.....
2 年级曾经筹备好了
5 年级曾经筹备好了
3 年级曾经筹备好了
6 年级曾经筹备好了
1 年级曾经筹备好了
4 年级曾经筹备好了
所有年级筹备好了,登程.........

主线程规定工夫期待子线程实现

学校的领导规定在 8 点汇合登程,当然才去的一年级小朋友,必然没有大哥哥整顿速度那么快,导致其余年级都走了,一年级才筹备好的场景,代码实现如下:

public class CountDownLatchExample3 {
    private final static int gradeNum = 6;
    public static void main(String[] args) throws Exception {ExecutorService exec = Executors.newScheduledThreadPool(gradeNum);
        final CountDownLatch countDownLatch = new CountDownLatch(gradeNum);
        for (int i = 0; i < gradeNum; i++) {
            int gradeName = i + 1;
            exec.submit(() -> {
                try {wait(gradeName);
                } catch (Exception e) { } finally {countDownLatch.countDown();
                }
            });
        }
        System.out.println("期待所有年级汇合筹备.....");
        countDownLatch.await(1000, TimeUnit.MILLISECONDS);
        System.out.println("8 点了筹备好的年级,先登程.........");
        exec.shutdown();}
    private static void wait(int gradeName) throws Exception {
        // 模仿一年级等待时间更长
        if (gradeName == 1) {TimeUnit.SECONDS.sleep(2);
        } else {TimeUnit.MILLISECONDS.sleep((int) Math.random() * 1000);
        }
        System.out.println(gradeName + "年级曾经筹备好了");
    }
}

与后面的代码相比,此段代码多了一个 countDownLatch.await(1000, TimeUnit.MILLISECONDS);,countDownLatchawait 能够期待超时工夫,如果在规定工夫内业务不论有没有实现,主线程都不会被阻塞。
运行后果如下:

期待所有年级汇合筹备.....
3 年级曾经筹备好了
4 年级曾经筹备好了
2 年级曾经筹备好了
6 年级曾经筹备好了
5 年级曾经筹备好了
8 点了筹备好的年级,先登程.........
1 年级曾经筹备好了

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

退出移动版