2.4、juc锁-程序计数器countDownLatch、CyclicBarrier

CountDownLatch和CyclicBarrier的区别

(1) CountDownLatch的作用是允许1或N个线程等待其他线程完成执行;而CyclicBarrier则是允许N个线程相互等待。

(2) CountDownLatch的计数器无法被重置;CyclicBarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。

(3) CountDownLatch 参与的线程的职责是不一样的,有的在倒计时,有的在等待倒计时结束。CyclicBarrier 参与的线程职责是一样的。

(4) CyclicBarrier 还可以设置回调函数, 回调函数执行在一个回合里最后执行await()的线程上, 为同步调用回调函数。一般会开启一个线程池异步处理回调函数。

一、countDownLatch

1、使用场景:

1: 某一线程在开始运行前等待n个线程执行完毕。

将CountDownLatch的计数器初始化为n:new CountDownLatch(n) ,每当一个任务线程执行完毕,就将计数器减1, countdownlatch.countDown(),当计数器的值变为0时,在CountDownLatch上 await() 的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。

一种典型的场景就是火箭发射。在火箭发射前,为了保证万无一失,往往还要进行各项设备、仪器的检查。 只有等所有检查完毕后,引擎才能点火。

2:实现多个线程开始执行任务的最大并行性。

注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。做法是初始化一个共享的CountDownLatch(1),将其计数器初始化为1,多个线程在开始执行任务前首先 coundownlatch.await(),当主线程调用 countDown() 时,计数器变为0,多个线程同时被唤醒。


2、简单介绍:

1、同步的辅助类,允许一个或多个线程,等待其他一组线程完成操作,再继续执行。

2、CountDownLatch是通过“共享锁”实现的。在创建CountDownLatch中时,会传递一个int类型参数count,该参数是“锁计数器”的初始状态,表示该“共享锁”最多能被count给线程同时获取。当某线程调用该CountDownLatch对象的await()方法时,该线程会等待“共享锁”可用时,才能获取“共享锁”进而继续运行。而“共享锁”可用的条件,就是“锁计数器”的值为0!而“锁计数器”的初始值为count,每当一个线程调用该CountDownLatch对象的countDown()方法时,才将“锁计数器”-1;通过这种方式,必须有count个线程调用countDown()之后,“锁计数器”才为0,而前面提到的等待线程才能继续运行!

3、实现原理:

 1)通过“共享锁”实现。CountDownLatch是通过一个计数器来实现的,计数器的初始值为需要等待线程的数量。

eg:CountDownLatch c = new CountDownLatch(10); // 等待线程的数量为10

 2)主线程调用CountDownLatch的await()方法会阻塞当前线程(即:主线程在闭锁上等待),直到计数器的值为0。

 3)当一个工作线程完成了自己的任务后,调用CountDownLatch的countDown()方法,计数器的值就会减1。

 4)当计数器值为0时,说明所有的工作线程都执行完了,此时,在闭锁上等待的主线程就可以恢复执行任务。

4、例子:

public class CountDownLatchTest1 {

    private static CountDownLatch doneSignal=new CountDownLatch(5);

    public static void main(String[] args) {

        try {

          for(int i=0; i<5; i++){

                new InnerThread().start();

                System.out.println("main await begin.");

                doneSignal.await();// "主线程"等待线程池中5个任务的完成

                System.out.println("main await finished.");

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

    static class InnerThread extends Thread{

        public void run() {

            try {

                Thread.sleep(1000);

                System.out.println(Thread.currentThread().getName() + " sleep 1000ms.");

                doneSignal.countDown(); // 将CountDownLatch的数值减1

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

        }

    }

}


main await begin.

Thread-0 sleep 1000ms.

Thread-2 sleep 1000ms.

Thread-1 sleep 1000ms.

Thread-4 sleep 1000ms.

Thread-3 sleep 1000ms.

main await finished.

结果说明:主线程通过doneSignal.await()等待其它线程将doneSignal递减至0。其它的5个InnerThread线程,每一个都通过doneSignal.countDown()将doneSignal的值减1;当doneSignal为0时,main被唤醒后继续执行。

5、源码分析:

稍后附加

https://www.cnblogs.com/skywang12345/p/3533887.html



二、CyclicBarrier

1、使用场景:

1)生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是 CyclicBarrier。

2)一个线程组的线程需要等待所有线程完成任务后再继续执行下一次任务。可以用于多线程计算数据,最后合并计算结果的场景。

例子:模拟多线程分组计算

有一个大小为50000的随机数组,用5个线程分别计算10000个元素的和,然后在将计算结果进行合并,得出最后的结果。各种应用例子

比如现在需要计算10个人12个月内的工资详细,可以将线程分为10个,分别计算每个人的工资,最后,再用barrierAction将这些线程的计算结果进行整合,得出最后结果。

3)举个报旅行团旅行的例子。

出发时,导游会在机场收了护照和签证,办理集体出境手续,所以,要等大家都到齐才能出发,出发前再把护照和签证发到大家手里。

对应CyclicBarrier使用。每个人到达后进入barrier状态。都到达后,唤起大家一起出发去旅行。旅行出发前,导游还会有个发护照和签证的动作。   https://www.jianshu.com/p/4ef4bbf01811

2、简单介绍:

CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。和CountDownLatch相似,也是等待某些线程都做完以后再执行。



3、实现原理:

1、CyclicBarrier是通过一个计数器来实现的,计数器的初始值为需要等待线程的数量。

eg:CyclicBarrier c = new CyclicBarrier(2); // 等待线程的数量为2

2)每个线程调用CyclicBarrier的await()方法,使自己进入等待状态。

3)当所有的线程都调用了CyclicBarrier的await()方法后,所有的线程停止等待,继续运行。

4、例子:


从打印结果可以看出,所有线程会等待全部线程到达栅栏之后才会继续执行,并且最后到达的线程会完成 Runnable 的任务。

5、源码分析:

https://www.cnblogs.com/skywang12345/p/3533995.html

稍后附加

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容