《蹲坑也能进大厂》多线程系列 – 轻松玩转CountDownLatch

前言

多线程系列我们前面已经更新过多个章节,强烈建议小伙伴按照顺序学习

《蹲坑也能进大厂》多线程系列文章目录****

CountDownLatchJUC中常见的一种工具类,从类名中我们也能略窥一二这个类的含义,CountDown字面意思为倒数,latch有门闩(shuan)的意思,主要是用来控制并发流程,本文将详细介绍它的场景和用法。

一、CountDownLatch两大适用场景

  • 控制子流程结束:一个线程等待多个线程执行完毕

经常网购的小伙伴对拼夕夕肯定不陌生吧,而拼夕夕有个特点,只有在商品拼团成功后,商家才会发货,如果一直没能成功,那你的订单会一直处于等待的状态;此外,游乐园里的过山车,一次可以坐10个人,商家为了节约成本,一般都是等待人满才会发车,这些都是一等多的生活场景。

image.png

  • 控制子流程开始:多个线程等待一个线程执行完毕

这种场景也是非常常见,比如我们以前参加运动会时,100米短跑时都会先抬起优秀的翘臀,等待裁判员发出指令后才能起跑,只要裁判不发指令,所有运动员都要进行等待。此外还有一种场景离我们也是十分近—压测,压测也是为了模拟多个请求同时访问某个接口,预测出其支持最大压力程度。不知道有没有小伙伴遇到服务上线后,因为流量激增服务被干掉的情况,哎,不说了,都是泪。

二、CountDownLatch常见方法

CountDownLatch主要是通过AQS的共享锁机制实现的,内部逻辑比较简单,它的核心属性只有一个sync,这个后续会AQS会详细介绍

private final Sync sync;
复制代码

现在我们只要掌握以下4个属性在两种场景下的作用即可:

  • CountDownLatch(int count): 类中唯一的构造器,count为倒数的次数;
  • await(): 调用该方法的线程会被挂起,直到count为0时才会被唤醒;
  • countDown(): 将count执行减一操作,当count为0时,等待中的线程会被唤醒;
  • getCount(): 获取latch数量。

三、CountDownLatch代码演示

  • 一等多场景

我们开发软件的目的是上线供用户使用,那再上线前肯定需要对系统进行全面测试,而很多模块间并没有依赖关系,因此可以同时进行测试,等待所以模块测试完毕,才能进行正式上线。

本例中假设有五个模块需要测试,并且模块测试可以同步进行,当每个模块测试完毕后会调用countDown方法,执行减一操作,等到count为0时,调用了await()方法的主线程从挂起状态被激活,继续执行。

public static void main(String[] args) throws InterruptedException {
    CountDownLatch latch = new CountDownLatch(5);
    ExecutorService service = Executors.newFixedThreadPool(5);
    //创建5个线程,分别执行不通模块测试
    for (int i = 1; i <= 5; i++) {
        final int num = i;
        Runnable runnable = () -> {
            try {
                //模拟测试耗时
                Thread.sleep((long) (Math.random()*5000));
                System.out.println("模块:"+ num +"测试完毕");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                //测试完毕后执行减一操作
                latch.countDown();
            }
        };
        service.submit(runnable);
    }
    System.out.println("等待模块检测...");
    //主线程等待,直到count为0
    latch.await();
    System.out.println("所有模块测试完毕,开始上线");
}
复制代码

运行结果,当所有模块检测完毕后才开始上线:

等待模块检测...
模块:2测试完毕
模块:5测试完毕
模块:4测试完毕
模块:3测试完毕
模块:1测试完毕
所有模块测试完毕,开始上线
复制代码

如果小伙伴还是觉得很绕,这里画个图就基本明白了。

  • 初始化count为5;
  • 主线程执行await()进入等待状态;
  • 各个模块调用countDown()方法对count减一,并且线程不会进入休眠,而是继续执行自身逻辑;
  • count变成 0 后,主线程被唤醒。

image.png

  • 多等一操作

这里使用运动会赛跑的栗子:在运动会上,所有运动员会先进行准备动作,只有等到裁判员发出信号,所有人才能开始比赛。

与之前的例子主要区别在于:CountDownLatch初始变量count为1,然后所有线程调用await方法进行等待,直到主线程调用countDown方法将count值减为1,此时所有线程同时被唤醒,开始执行自身逻辑。

public static void main(String[] args) throws InterruptedException {
    //重点:这里初始化为1
    CountDownLatch begin = new CountDownLatch(1);
    
    ExecutorService service = Executors.newFixedThreadPool(5);
    //创建5个线程
    for (int i = 1; i <= 5; i++) {
        final int num = i;
        Runnable runnable = () -> {
            try {
                System.out.println("运动员:"+ num +"等待起跑指令");
                //所有运动员等待指令
                begin.await();
                System.out.println("运动员:"+ num +"开始跑步");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        };
        service.submit(runnable);
    }
    Thread.sleep(1000);
    //裁判员发出起步指令
    begin.countDown();
    System.out.println("发令完毕");
}
复制代码

运行结果,当发令法币后所有运动员才开始起跑:

运动员:1等待起跑指令
运动员:3等待起跑指令
运动员:4等待起跑指令
运动员:2等待起跑指令
运动员:5等待起跑指令
发令完毕
运动员:3开始跑步
运动员:4开始跑步
运动员:1开始跑步
运动员:5开始跑步
运动员:2开始跑步
复制代码

总结

以上就是CountDownLatch的全部内容,相对来说比较容易掌握,理解难度不高,结合具体应用场景选择两种方式来使用,今天的文章就到这里咯,下一期唠唠其他好用的工具类吧,好好敲一敲,轻松搞定并发编程。

点关注,防走丢

以上就是本期全部内容,如有纰漏之处,请留言指教,非常感谢。我是花GieGie ,有问题大家随时留言讨论 ,我们下期见?。

文章持续更新,可以微信搜一搜 Java开发零到壹 第一时间阅读,并且可以获取面试资料学习视频等,有兴趣的小伙伴欢迎关注,一起学习,一起哈??。

原创不易,你怎忍心白嫖,如果你觉得这篇文章对你有点用的话,感谢老铁为本文点个赞、评论或转发一下,因为这将是我输出更多优质文章的动力,感谢!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享