CountDownLatch原理解析

1. demo展示

代码逻辑展示了主线程中创建2个子线程分别去执行任务,主线程等2个子线程执行完毕后,再接着执行下面的代码;

常用场景:

分别计算,汇总结果。如,多个线程分别解析excel中的sheet,等待全部解析完毕后汇总结果;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class CountDownLatchDemo {
    //定义一个倒计时闩锁
    static CountDownLatch c = new CountDownLatch(2);

    public static void main(String[] args) throws InterruptedException {
        new Thread(() -> {
            try {
                TimeUnit.MICROSECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我是线程1");
            //释放一个
            c.countDown();
        }).start();

        new Thread(() -> {
            try {
                TimeUnit.MICROSECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("我是线程2");
            //释放一个
            c.countDown();
        }).start();

        System.out.println("我是主线程,我要等那两个线程执行完毕...");
        //等待倒计时为0
        c.await();
        System.out.println("我是主线程,那两个线程都执行完了");
    }
}

输出:

我是主线程,我要等那两个线程执行完毕...

我是线程2

我是线程1

我是主线程,那两个线程都执行完了

2. 原理解析

1.先看构造函数new CountDownLatch(2)做了什么?

这是初始化了AQS子类,并将AQS的状态state设置为传入的2;

public CountDownLatch(int count) {
      if (count < 0) throw new IllegalArgumentException("count < 0");
      this.sync = new Sync(count);
  }

2.看c.countDown()做了什么?

它释放了一个共享锁状态,也就是state减1;

public void countDown() {
     sync.releaseShared(1);
 }

3.再看c.await()做了什么?

await方法是CounDownLatch中定义的,它调用了其内部类Sync(也是AQS的子类)的获取共享锁的方法acquireSharedInterruptibly;

acquireSharedInterruptibly方法中调用了CountDownLatch内部类Sync中实现的获取共享锁的方法tryAcquireShared,返回值不小0就算获取到了锁,await方法就能返回了,如果返回值小于0将会进入阻塞等待;

CountDownLatch内部类Sync中tryAcquireShared的实现很简单,只要state=0就返回1,否则返回-1;上面说了返回一个不小于0的数字,c.await()就相当于获取到了锁,就可以返回了,主线程就可以继续执行了。

通过上面分析,每次c.countDown(),就会将state减1,state=0的时候主线程恢复执行; 

Java CountDownLatch学习总结

来源包

同为 java.util.concurrent 下的,即也是并发多线程相关下的类,直译 “倒计时锁存器”,一般用于多线程场景,单一的线程也可以,用于等待多个任务完成后再执行其他操作;

提供方法

await()

  • 导致当前线程等待,直到锁存器倒数到零,除非该线程是{@linkplain Thread35;interrupt interrupted}即被打断状态。
  • 如果当前计数为零,则此方法立即返回。
  • 如果当前计数大于零,则当前线程将出于线程调度目的被禁用,并处于休眠状态,直到发生以下两种情况之一:
  • 由于调用{@link#countDown}方法,计数达到零;或者其他线程{@linkplain thread#中断}当前线程。

如果当前线程:

  • 在进入此方法时设置了其中断状态;或者
  • 在等待时{@linkplain Thread#interrupt interrupted},
  • 则抛出{@link InterruptedException},并清除当前线程的中断状态。

简单说就是当使用了这个方法后当前这一个线程将进入等待状态,直到计数器被减到0或者当前线程被中断,计数器被减到0后,所有等待的线程将被唤醒继续向下执行

await(long timeout, TimeUnit unit)

同上,但是指定了等待的超时时间,即线程除了上方两种被唤醒的情况下,等待到超时时间后也会被唤醒

  • countDown():当前计数器减一,如果如果减到 0 则唤醒所有等待在这个 CountDownLatch 上的线程。
  • getCount():获取当前计数的数值

业务书写示例

即将需要一会儿处理的业务 list 设置为计数器的大小,

然后对里面的业务数据执行异步操作,处理业务过程中不论是否有异常都需要对计数器减一,最终使用 await 等待所有任务执行完成,执行完成后,将进入后续处理

            final CountDownLatch latch = new CountDownLatch(lists.size());
            
            for (List<JSONObject> item: lists) {
                executor.submit(new Runnable() {
                    @Override
                    public void run() {
                        // ....... 业务处理
                        } catch (Exception e) {
                            // 异常处理
                        } finally {
                            latch.countDown();
                        }
                    }
                });
            }

            try {
                latch.await();
            } catch (InterruptedException e) {
                log.error("线程被中断", e);
            }

       // lists 处理完成后的其他业务操作

一般代码示例

public static void main(String[] args) throws InterruptedException {
        final CountDownLatch downLatch = new CountDownLatch(3);

        Await wait111 = new Await("wait111", downLatch);
        Await wait222 = new Await("wait222", downLatch);
        CountDownStart countDownStart = new CountDownStart(downLatch);

        wait111.start();
        wait222.start();
        Thread.sleep(1000);
        countDownStart.run();

    }


class Await extends Thread{

    private CountDownLatch countDownLatch;
    private String name;

    public Await(String name, CountDownLatch countDownLatch){
        this.name = name;
        this.countDownLatch = countDownLatch;
    }


    @Override
    public void run() {
        System.out.println(name   " start.....");
        System.out.println(name   " run.....");
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(name   " continue.....run");
    }
}

class CountDownStart extends Thread{

    private CountDownLatch countDownLatch;

    public CountDownStart(CountDownLatch countDownLatch){
        this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
        countDownLatch.countDown();
        countDownLatch.countDown();
        countDownLatch.countDown();
        System.out.println("start countDown");
    }
}

运行结果: 

wait222 start.....
wait222 run.....
wait111 start.....
wait111 run.....
start countDown
wait111 continue.....run
wait222 continue.....run
 

但是当我把线程等待去除后:

    public static void main(String[] args) throws InterruptedException {
        final CountDownLatch downLatch = new CountDownLatch(3);

        Await wait111 = new Await("wait111", downLatch);
        Await wait222 = new Await("wait222", downLatch);
        CountDownStart countDownStart = new CountDownStart(downLatch);

        wait111.start();
        wait222.start();
//        Thread.sleep(1000);
        countDownStart.run();

    }

结果:

start countDown
wait111 start.....
wait111 run.....
wait111 continue.....run
wait222 start.....
wait222 run.....
wait222 continue.....run

另外两个线程线程并没有开始就执行,可能被抢占了,也可能调度优先度不同,实际使用时还是需要多多实验

以上为个人经验,希望能给大家一个参考,也希望大家多多支持Devmax。

Java之CountDownLatch原理全面解析的更多相关文章

  1. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  3. VUE响应式原理的实现详解

    这篇文章主要为大家详细介绍了VUE响应式原理的实现,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助

  4. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  5. Java实现世界上最快的排序算法Timsort的示例代码

    Timsort 是一个混合、稳定的排序算法,简单来说就是归并排序和二分插入排序算法的混合体,号称世界上最好的排序算法。本文将详解Timsort算法是定义与实现,需要的可以参考一下

  6. Java日期工具类的封装详解

    在日常的开发中,我们难免会对日期格式化,对日期进行计算,对日期进行校验,为了避免重复写这些琐碎的逻辑,我这里封装了一个日期工具类,方便以后使用,直接复制代码到项目中即可使用,需要的可以参考一下

  7. Java设计模式之模板方法模式Template Method Pattern详解

    在我们实际开发中,如果一个方法极其复杂时,如果我们将所有的逻辑写在一个方法中,那维护起来就很困难,要替换某些步骤时都要重新写,这样代码的扩展性就很差,当遇到这种情况就要考虑今天的主角——模板方法模式

  8. Java 中 Class Path 和 Package的使用详解

    这篇文章主要介绍了Java 中 Class Path和Package的使用详解,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的朋友可以参考一下

  9. java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源)

    这篇文章主要介绍了java SpringBoot 分布式事务的解决方案(JTA+Atomic+多数据源),文章围绕主题展开详细的内容介绍,具有一定的参考价值,感兴趣的小伙伴可以参考一下

  10. 五分钟理解keep alive用法及原理

    这篇文章主要为大家介绍了keep alive用法及原理示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部