• [笔记][Java7并发编程实战手冊]3.4 等待多个并发事件的完毕CountDownLatch倒计数闭锁


    [笔记][Java7并发编程实战手冊]系列文件夹


    简单介绍

    本文学习CountDownLatch 倒计数闭锁。
    本人英文不好。靠机器翻译,然后有一段非常形象的描写叙述,让我把它叫为倒计数

    用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法。所以在当前计数到达零之前。await 方法会一直受堵塞。

    之后,会释放全部等待的线程,await 的全部兴许调用都将马上返回。这样的现象仅仅出现一次——计数无法被重置。 - jdk1.6里面的文档

    能够看出,是由于会倒计数。并且计数器到0之后, 就不能再次使用该对象了(自断后路)。所以称为倒计数闭锁


    CountDownLatch

    是一个同步辅助类,在完毕一组正在其它线程中执行的操作之前,它同意一个或多个线程一直等待。
    假如说:有a、b、c 三个线程。c 须要a,b线程的结果,所以C 须要等待 a,b 线程都执行完了,b 才干拿到正确的结果。 就是该类的一个使用场景

    1. countDown() 来计数(计时)
    2. await() 创建屏障点。等待计数到0的时候,await()方法后面的代码才执行。(换句话说。计数线程一起冲破这个屏障)
    3. getCount(): 是获取当前计数,不是总数
    4. 是辅助类! 不保证同步。

    原理:
    CountDownLatch是通过“共享锁”实现的。

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


    演示样例

    场景:密室大门将在七颗龙珠都被镶嵌到大门上的机关上时,密室大门才会打开。

    以下的演示样例:讲述了一个密室(线程)。和7个工匠(线程)镶嵌龙珠, 最后密室大门被打开,守卫(主线程)复活的故事

    /**
     * Created by zhuqiang on 2015/8/19 0019.
     */
    public class Client {
        public static void main(String[] args) {
            final CountDownLatch cd = new CountDownLatch(7);  //创建辅助类7个倒计数
            final Adytum adytum = new Adytum(cd);
            new Thread(adytum).start();
            for (int i = 0; i < 7; i++) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        adytum.inlay();
                    }
                }).start();
            }
    
            // 主线程 来模拟守卫吧。
            try {
                cd.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("守卫也复活了!");
        }
    
    }
    
    //密室
    class Adytum implements Runnable {
        private CountDownLatch cd;
        private long num = 7;  // 记录剩余未被镶嵌的龙珠 ,用来对照和getCount返回的是否相等
        private ReentrantLock lock = new ReentrantLock();   //创建同步锁。保证 inlay 中的资源计数 被正确计算。

    public Adytum(CountDownLatch cd) { this.cd = cd; } // 为密室大门服务的线程 @Override public void run() { System.out.println("欢迎来到密室。密室正等待被打开..."); try { cd.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.printf("当前龙珠已镶嵌%d颗。密室被打开了!欢呼吧 ",num); } /** * 镶嵌龙珠到大门上 */ public void inlay() { try { lock.lock(); // 把这里的锁去掉,能看到错误的计算数据,说明了CountDownLatch:不保证同步 long count = cd.getCount(); //返回当前还剩余计数 long time = (long) (Math.random() * 10); //模拟镶嵌了多少时间 System.out.printf("%s把龙珠镶嵌到了大门上,还有%d个龙珠未被镶嵌,计数是否和getCount相等:%s,此次镶嵌耗时:%d 秒 ", Thread.currentThread().getName(), count,count==num,time); num--; TimeUnit.SECONDS.sleep(time); //休眠辅助类。按指定单位休眠 cd.countDown(); //锁 保证是这个临界区内的代码同步。线程相互排斥,计算的结果正确被刷到主内存中。 不是说countDown方法须要来同步 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }

    未加锁的某一次执行结果:
    把 lock.lock(); 加锁相关的代码凝视掉。

    欢迎来到密室。密室正等待被打开...
    Thread-1把龙珠镶嵌到了大门上,还有7个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:5 秒
    Thread-2把龙珠镶嵌到了大门上,还有7个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:2 秒
    Thread-4把龙珠镶嵌到了大门上,还有7个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:0 秒
    Thread-3把龙珠镶嵌到了大门上,还有7个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:1 秒
    Thread-6把龙珠镶嵌到了大门上,还有7个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:7 秒
    Thread-5把龙珠镶嵌到了大门上,还有7个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:9 秒
    Thread-7把龙珠镶嵌到了大门上,还有7个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:0 秒
    守卫也复活了!
    当前龙珠已镶嵌0颗,密室被打开了!欢呼吧
    

    说明:能看到错误的计算数据,全是还有7个龙珠。说明了CountDownLatch:不保证同步,仅仅是达到 让一个线程或则多个线程等待


    加锁的正确结果

    欢迎来到密室,密室正等待被打开...
    Thread-1把龙珠镶嵌到了大门上,还有7个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:2 秒
    Thread-2把龙珠镶嵌到了大门上,还有6个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:0 秒
    Thread-3把龙珠镶嵌到了大门上,还有5个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:5 秒
    Thread-4把龙珠镶嵌到了大门上,还有4个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:5 秒
    Thread-5把龙珠镶嵌到了大门上,还有3个龙珠未被镶嵌。计数是否和getCount相等:true,此次镶嵌耗时:0 秒
    Thread-6把龙珠镶嵌到了大门上,还有2个龙珠未被镶嵌。计数是否和getCount相等:true,此次镶嵌耗时:2 秒
    Thread-7把龙珠镶嵌到了大门上,还有1个龙珠未被镶嵌,计数是否和getCount相等:true,此次镶嵌耗时:5 秒
    当前龙珠已镶嵌0颗,密室被打开了!欢呼吧
    守卫也复活了!

    说明:有密室,和守卫两个线程等待7个工匠线程把龙珠镶嵌完毕后,密室被打开,守卫也复活,得到了我们想要的正确结果

  • 相关阅读:
    Android编译环境配置(Ubuntu 14.04)
    Android中的接口回调技术
    我为什么要拒绝Ctrl+C和Ctrl+V?
    软件设计网站大全
    国内及Github优秀开发人员列表
    Linux常用指令
    Linux系统目录结构
    UML图
    Android软件设计---Dumpsys工具使用
    Android应用程序Monkey测试
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/7084877.html
Copyright © 2020-2023  润新知