ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Java并发工具学习(九)——CountDownLatch和CyclicBarrier

2021-11-28 22:03:16  阅读:131  来源: 互联网

标签:Java System 线程 CountDownLatch println CyclicBarrier out


文章目录

前言

继续简单总结一下Java中并发流程控制的工具类,这一篇小结一下CountDownLatch和CyclicBarrier

CountDownLatch

Latch中文是门闩的意思,CountDown是倒数,倒数到0,门闩开启。从名称可以看出这个工具的使用场景。比如在某多多上拼团购物,5人成团,等到成团了才有资格购买。

通常CountDownLatch有两种常见的用法

1、一个线程等待其他多个线程倒数为0时,再进入下一步

在这里插入图片描述
上图形象表示了CountDownLatch的一种使用方法,由其他线程倒数到0了之后,图中的Ta线程才能继续进行下一步。
简单代码实例:模拟一个产品需要5个质检员完成质检才能发布

/**
 * autor:liman
 * createtime:2021/11/28
 * comment:模拟工厂质检
 * 一个产品的发布,需要5个人完成质检
 */
@Slf4j
public class CountDownLatchDemo01 {

    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(5);//需要5个人完成质检
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final int number = i+1;
            Runnable runnable=new Runnable(){
                @Override
                public void run() {
                    try {
                        //模拟质检的时间
                        long checkTime = (long) (Math.random() * 10000);
                        Thread.sleep(checkTime);
                        System.out.println("No."+number+"完成了质检,耗时:"+checkTime+"ms");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        //一个线程完成质检,就调用countDown方法,直到CountDownLatch的数值为0,则主线程就可以执行
                        countDownLatch.countDown();
                    }
                }
            };
            executorService.submit(runnable);
        }
        System.out.println("等待5个质检员检查完毕");
        //主线程等待5个子线程完成质检,调用CountDownLatch的await方法
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("所有人都完成了工作,产品可以对外发布");
        //停止线程池
        //判断线程池是否停止
        executorService.shutdown();
        while(!executorService.isTerminated()){
            //线程池没有执行完成任务,主线程就空转,直到线程池运行结束
        }
    }
}

2、多个线程等待一个线程倒数为0,再进行下一步。

这里通过一个比较复杂的综合实例来说明,如以下代码,模拟了一个运动长跑的简单实例,采用了两个CountDownLatch对象。相关代码解释已在注释中

/**
 * autor:liman
 * createtime:2021/11/28
 * comment:模拟运动会的运动员跑步
 * 多个运动员等待统一指令起跑
 * 同时所有运动员都到终点的时候才能结束比赛
 */
@Slf4j
public class CountDownLatchDemo02 {

    public static void main(String[] args) throws InterruptedException {
        //初始化启动的CountDownLatch,指定个数为1
        CountDownLatch beginCountDownLatch = new CountDownLatch(1);
        //初始化结束的CountDownLatch,指定个数为5
        CountDownLatch endCountDownLatch = new CountDownLatch(5);
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        for (int i = 0; i < 5; i++) {
            final int no = i+1;
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("No."+no+"准备完毕,等待发令枪发令");
                    try {
                        beginCountDownLatch.await();//子线程会在这里阻塞,等待主线程发出指令
                        System.out.println("No."+no+"开始跑步");
                        long runTime = (long) (Math.random() * 10000);
                        Thread.sleep(runTime);//模拟随机的跑步时长,执行完毕之后,表示跑到终点
                        System.out.println("No."+no+"到达终点,耗时:"+runTime+"ms");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        //到达终点,发出信号
                        endCountDownLatch.countDown();
                    }
                }
            };
            executorService.submit(runnable);
        }
        //主线程其实就是裁判,模拟裁判准备工作
        Thread.sleep(5000);
        System.out.println("发令枪响,所有运动员开始跑步");
        beginCountDownLatch.countDown();
        endCountDownLatch.await();//主线程阻塞,裁判员继续等待所有运动员到达终点。
        System.out.println("所有人都到终点,比赛结束");
        //判断线程池是否停止
        executorService.shutdown();
        while(!executorService.isTerminated()){
            //线程池没有执行完成任务,主线程就空转,直到线程池运行结束
        }
    }
}

上述实例中的等待5个运动员到达终点,才能结束比赛,这一点和第一种场景,有五个质检员都完成质检,产品才能发布是一样的。

基于上述两个常用的使用场景,其实CountDownLatch也支持一组线程等待另一组线程执行完毕后的情况。

需要注意的是:CountDownLatch是不能复用的,如果要重新计数,可以考虑用CyclicBarrier或者重新创建CountDownLatch。

CyclicBarrier

Cyclic中文为循环,Barrier是栅栏的意思,循环栅栏说明这个是可复用的。这个其实在实际的使用场景中是和CountDownLatch很相似的。

直接上实例吧

/**
 * autor:liman
 * createtime:2021/11/28
 * comment:循环栅栏的实例
 * 相比于CountDownLatch,CyclicBarrier是可重用的
 */
@Slf4j
public class CyclicBarrierDemo {

    public static void main(String[] args) {
        //实例化一个CyclicBarrier,第二个参数是所有的线程都到齐了之后,CyclicBarrier的操作
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
            @Override
            public void run() {
                System.out.println("所有人都到了,大家统一出发");
            }
        });

        for (int i = 0; i < 10; i++) {
            //启动每一个线程
            new Thread(new Task(i, cyclicBarrier)).start();
        }
    }

    static class Task implements Runnable {

        private int id;
        private CyclicBarrier cyclicBarrier;

        public Task(int id, CyclicBarrier cyclicBarrier) {
            this.id = id;
            this.cyclicBarrier = cyclicBarrier;
        }

        @Override
        public void run() {
            System.out.println("线程" + id + "现在前往集合地点");
            try {
                Thread.sleep((long) (Math.random() * 10000));
                System.out.println("线程" + id + "到了集合地点,开始等待其他线程到达");
                cyclicBarrier.await();//等待其他线程到达。
                System.out.println("线程" + id + "出发");//所有线程都就位了,就会一起执行这个代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果
在这里插入图片描述

二者的区别

二者使用场景很相似,但还是存在些许差异
1、二者作用不同。CyclicBarrier要等到固定数量的线程到达了指定位置才能继续执行(CountDownLatch好像也有这个作用),但是CountDownLatch只需要等待设定的数值为0之后,就可以执行,毕竟一个线程中也可以多次进行倒数,CountDownLatch关注的是事件,而CyclicBarrier关注的才是线程。
2、可重用性不同。CountDownLatch在倒数到0时,就不能再次使用了,除非新建实例,而CyclicBarrier是可重用的,上述CyclicBarrier的实例中,虽然CyclicBarrier初始化的数值为5,但是循环启动了10个线程,依旧可重新使用。

总结

简单梳理了一下二者的使用场景,后续开始总结AQS的内容

标签:Java,System,线程,CountDownLatch,println,CyclicBarrier,out
来源: https://blog.csdn.net/liman65727/article/details/121594243

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有