java CountDownLatch 和 CyclicBarrier

一、CountDownLatch

        AQS (AbstractQueuedSynchronizer)是专属于构造锁和同步器的一个抽象工具类,基于它 Java 构造出了大量的常用同步工具,如 ReentrantLock、Semaphore、ReentrantReadWriteLock、SynchronousQueue 等等,CountDownLatch 同样如此。
        CountDownLatch (倒时器) 允许 N 个线程阻塞在同一个地方,直至所有线程的任务都执行完毕。
        CountDownLatch 有一个计数器,可以通过 countDown () 方法对计数器的数目进行减一操作,也可以通过 await () 方法来阻塞当前线程,直到计数器的值为 0。

CountDownLatch 内部原理

在这里插入图片描述
         几乎所有基于 AQS 构造的同步类,内部都需要一个静态内部类去继承 AQS,并实现其提供的钩子方法,通过封装 AQS 中的 state 为 count 来确定多个线程的计时器。

countDown () 方法

         在 CountDownLatch 中通过 countDown 来减少倒计时数,这是最重要的一个方法,对于减一操作是由 CountDownLatch 内部类 Sync 完成
在这里插入图片描述

await () 方法

         除了 countDown () 方法外,在 CountDownLatch 中还有一个重要方法await () 方法,CountDownLatch 提供了两个 await() 方法,一个无参数,一个有参数,有参数的方法可以配置带有时间参数的,表示最大阻塞时间,当调用 await () 的时候,如果 state 不为 0,那就证明任务还没有执行完毕,await () 就会一直阻塞,也就是说 await () 之后的语句不会被执行。然后,CountDownLatch 会自旋 CAS 判断 state 是否等于 0,若是就会释放所有等待的线程,await () 方法之后的语句得到执行。
在这里插入图片描述

CountDownLatch 使用

         模拟多个任务执行,前置任务完成后再执行最后的任务

 /**
     * 模拟多个任务执行,前置任务完成后在执行最后的任务
     * @param args
     */
    public static void main(String[] args) throws InterruptedException {
        //前置任务数
        int beforeTaskNum = 5;
        CountDownLatch countDownLatch = new CountDownLatch(beforeTaskNum);
        //模拟前置任务
        for (int i = 0; i < beforeTaskNum; i++) {
            int name = (i+1);
            new Thread(() -> {
                Long startTime = (new Date()).getTime();
                System.out.println("任务 "+name+" 执行中... ...");
                //模拟不同任务执行需要的时间
                try {
                    Thread.sleep(name*1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Long endTime = (new Date()).getTime();
                System.out.println("任务 "+name+" 执行完成,耗时 "+(endTime-startTime)+" ms");
                //任务完成后减一
                countDownLatch.countDown();
            }).start();
        }

        //等待前置任务完成
        countDownLatch.await();
        System.out.println("所有执行完成... ...");
        System.out.println("最终任务执行中... ...");
    }

         结果

任务 5 执行中... ...
任务 4 执行中... ...
任务 2 执行中... ...
任务 1 执行中... ...
任务 3 执行中... ...
任务 1 执行完成,耗时 1010 ms
任务 2 执行完成,耗时 2015 ms
任务 3 执行完成,耗时 3011 ms
任务 4 执行完成,耗时 4007 ms
任务 5 执行完成,耗时 5006 ms
所有执行完成... ...
最终任务执行中... ...

二、CyclicBarrier

        CyclicBarrier 是 Java 中的一个同步辅助类,它允许一组线程在达到某个同步点之前进行相互等待,然后同时继续执行。它的主要作用是在多个线程之间创建一个同步点,当所有线程都到达这个点时,它们才能继续执行。它的计数是循环的,当所有线程都到达同步点后,计数会重置,可以继续使用。

CyclicBarrier 有两个构造方法:

在这里插入图片描述

  • parties:指定需要等待的数量
  • barrierAction:到同步点后的动作

await () 方法

让线程在达到同步点时等待,直到所有参与者线程都到达同步点,然后才能继续执行。
与 CountDownLatch 类似也提供了两个 await() 方法
在这里插入图片描述

CyclicBarrier 使用

 public static void cyclicBarrier(){
        int parties = 3;
        // 创建一个CyclicBarrier,指定需要等待的线程数量和达到同步点后的动作
        CyclicBarrier cyclicBarrier = new CyclicBarrier(parties,new Thread(() -> {
            System.out.println("后续任务执行中... ...");
        }));
        // 创建并启动多个线程
        for (int i = 0; i < parties; i++) {
            new Thread(() -> {
                for (int j = 0; j < 3; j++) {
                    System.out.println(Thread.currentThread().getName() + " is waiting at the barrier");
                    try {
                        // 线程等待在barrier上
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " has crossed the barrier");
                }

            }).start();
        }

    }

在这里插入图片描述
重结果中可以看出当所有线程都到达同步点后,计数会重置,然后又可以继续使用。

三、CountDownLatch 与 CyclicBarrier 的区别

  1. 计数方式 :
  • CountDownLatch 的作用是允许一个或多个线程等待一组操作完成后再继续执行。它的计数是一次性的,也就是说计数减到 0 之后,就不能再次重置,除非创建一个新的 CountDownLatch 实例。

  • CyclicBarrier 的作用是在多个线程之间创建一个同步点,当所有线程都到达这个点时,它们才能继续执行。它的计数是循环的,当所有线程都到达同步点后,计数会重置,可以继续使用。

  1. 复用性 :
  • CountDownLatch 在计数减到 0 后就不能再次使用,除非创建一个新的实例。

  • CyclicBarrier 可以被重用,当所有线程到达同步点后,计数会重置,可以继续使用。

  1. 动作 :

    • CountDownLatch 本身不提供额外的动作,只是在计数为 0 时唤醒等待的线程。

    • CyclicBarrier 在所有线程到达同步点后,可以执行额外的动作,比如执行一个回调函数。

  2. 适用场景 :

  • CountDownLatch 适合用于一组线程等待另一组线程执行完毕后再继续执行的场景。

  • CyclicBarrier 适合用于一组线程之间需要相互等待,然后同时继续执行的场景。

总之,CountDownLatchCyclicBarrier 都是用于多线程协同的工具,但它们在计数方式、复用性、动作和适用场景上有一些区别,开发人员在选择使用时需要根据具体的需求和场景进行选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值