CountDownLatch详解-线程顺序控制

    科技2025-08-29  15

    CountDownLatch详解-线程顺序控制

    一、概念

    1、countDownLatch是在java1.5被引入,存在于java.util.cucurrent包下。

    2、countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。

    3、是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

    二、CountDownLatch 常用方法说明

    CountDownLatch(int count); //构造方法,创建一个值为count 的计数器。

    await();//阻塞当前线程,将当前线程加入阻塞队列。

    await(long timeout, TimeUnit unit);//在timeout的时间之内阻塞当前线程,时间一过则当前线程可以执行,

    countDown();//对计数器进行递减1操作,当计数器递减至0时,当前线程会去唤醒阻塞队列里的所有线程。

    三、测试代码

    业务场景:

    统计用户数据、订单数和商品情况,然后统一处理返回结果。如果每个业务场景预计要5秒,单线程情况需要15秒左右,如果用多线程处理,费时5秒左右。在多线程情况下,如何保证多线程统一执行完毕并返回结果呢?

    private static Map<String,Object> map = new HashMap<>(); //初始化计数器3 private static CountDownLatch countDownLatch = new CountDownLatch(3); public static void main(String[] args) { long start = System.currentTimeMillis(); System.out.println("开始时间:"+start); //统计新增用户 Thread userThread = new Thread(new Runnable() { @Override public void run() { try { System.out.println("线程一:统计新增用户开始"); Thread.sleep(5000); map.put("newUser",10); System.out.println("线程一:统计新增用户结束"); //计数器减一 countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }); //统计订单 Thread orderThread = new Thread(new Runnable() { @Override public void run() { try { System.out.println("线程二:统计订单开始"); Thread.sleep(5000); map.put("orderNum",20); System.out.println("线程二:统计订单结束"); //计数器减一 countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }); //统计商品 Thread goodThread = new Thread(new Runnable() { @Override public void run() { try { System.out.println("线程三:统计商品开始"); Thread.sleep(5000); map.put("goodCount",100); System.out.println("线程三:统计商品结束"); //计数器减一 countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } } }); userThread.start(); orderThread.start(); goodThread.start(); try { //主线程等待,countDownLatch为0的时候,也就是上面线程全都执行完毕,再执行下面的业务 countDownLatch.await(); System.out.println("统计完毕,统计结果:"+ JSON.toJSONString(map)); long end = System.currentTimeMillis(); System.out.println("结束时间:"+end+";耗时:"+(end-start)); } catch (Exception e) { e.printStackTrace(); } }

    执行结果一

    主线程会等到其他统计线程执行完毕以后才执行

    开始时间:1601634626698 线程一:统计新增用户开始 线程二:统计订单开始 线程三:统计商品开始 线程二:统计订单结束 线程一:统计新增用户结束 线程三:统计商品结束 统计完毕,统计结果:{"goodCount":100,"newUser":10,"orderNum":20} 结束时间:1601634631811;耗时:5113

    如果注释 countDownLatch.await();会有什么情况呢?

    执行结果二

    开始时间:1601634798801 线程一:统计新增用户开始 线程二:统计订单开始 线程三:统计商品开始 统计完毕,统计结果:{} 结束时间:1601634798957;耗时:156 线程一:统计新增用户结束 线程三:统计商品结束 线程二:统计订单结束

    await()方法会让当前线程等待,只有countDownLatch的计数器为0时才会执行。如果因为程序异常计数器永远不为0,当前线程就会一会等待。可以考虑使用await(long timeout, TimeUnit unit);等待一段时间后执行。

    使用场景:

    A业务依赖B、C等业务处理完毕后再执行,可以考虑使用countDownLatch

    关注公众号免费领取学习资料~

    Processed: 0.012, SQL: 8