Java多线程六脉神剑-少商剑CountDownLatch

前言

少商剑:剑路雄劲,石破天惊,CountDownLatch以其强大而直接的方式控制线程等待和同步,具有明确而有力的作用。

CountDownLatch是一个同步工具类,它是根据计数器实现的,构造函数初始时会指定总的计数数量,每调用一次countDown数量会减一,当数量为0时,闸门将会放开,await等待的线程进而继续执行。

例如,小红,小兰,小明一起去野餐,他们约定先一起到公园门口再开始活动,这时计数器初始值就是3,当小红到达目的地,计数器减1,小红继续等待小兰和小明;小兰到达计数器再减1,再一起等待小明的到达;小明到达,数量就减为0了,他们三人再一起进行活动。

一个简单的动画流程:

CountDownLath方法详解

  • 构造函数 CountDownLatch(int count):count为计数器的初始值(一般count就是线程数)
  • countDown():每调用一次count数减1(一般当线程完成任务后调用)
  • getCount():获取当前计数器count的值。
  • await():一直等待,直到计数器count值为0。
  • boolean await(long timeout, TimeUnit unit):一直等待,直到count值为0,或者过了timeout时间后停止等待。如果是count值为0停止的等待,返回的boolean就为true;如果是过了timeout时间后停止的等待,返回的boolean就为false。

举个栗子🌰

组装加工一台电脑,我们需要加工CPU、主板、内存、显卡、电源等部件,如果进行串行加工,那需要等待CPU加工完再加工主板,那耗时就是所有部件的总和,但如果像工厂那样,流水线作业并行处理,加工CPU的同时,其他所有部件也一起加工,等待所有的部件加工完,我们再组装成电脑,这样效率快了很多。

csharp 复制代码
public class CountDownLatchCase {
    public static void produce() throws InterruptedException {
        List<Task> taskList = new ArrayList<>();
        taskList.add(new Task("CPU",2000L));
        taskList.add(new Task("主板",800L));
        taskList.add(new Task("内存",500L));
        taskList.add(new Task("显卡",1000L));
        taskList.add(new Task("电源",200L));
        CountDownLatch latch = new CountDownLatch(taskList.size());
        ConcurrentHashMap<String,String> results = new ConcurrentHashMap<>();
        for (int i = 0; i < taskList.size(); i++) {
            Task task = taskList.get(i);
            new Thread(() -> {
                try {
                    Thread.sleep(task.getTime());
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                latch.countDown();
                String threadName = Thread.currentThread().getName();
                results.put(threadName,task.getName());
                System.out.println(threadName+"完成加工:"+task.getName()+",耗时:"+task.getTime()+"毫秒");
            },"线程"+(i+1)).start();
        }
        latch.await();
        for (Map.Entry<String, String> entry : results.entrySet()) {
            System.out.println("总部收到了来自"+entry.getKey()+"加工的"+entry.getValue());
        }
        if(results.size() == taskList.size()){
            System.out.println("所有部件加工完成,可以组装电脑");
        }
    }

    public static void main(String[] args) throws InterruptedException {
        produce();
    }
}

运行结果

makefile 复制代码
线程5完成加工:电源,耗时:200毫秒
线程3完成加工:内存,耗时:500毫秒
线程2完成加工:主板,耗时:800毫秒
线程4完成加工:显卡,耗时:1000毫秒
线程1完成加工:CPU,耗时:2000毫秒
总部收到了来自线程4加工的显卡
总部收到了来自线程5加工的电源
总部收到了来自线程2加工的主板
总部收到了来自线程3加工的内存
总部收到了来自线程1加工的CPU
所有部件加工完成,可以组装电脑

其他使用场景

  • 统计大屏页面数据时,把各个模块的数据使用多线程统计出来后,封装之后再一起返给前端。
  • 文件处理时,同时启动多个线程分别处理不同的文件,多线程把所有文件处理完毕后,再进行汇总和分析。
  • 系统启动时多线程加载配置文件、初始化数据库连接等操作,当这些操作完成后,业务处理线程才能开始工作。
相关推荐
江梦寻29 分钟前
MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)
开发语言·后端·python·macos·架构·策略模式
风象南36 分钟前
SpringBoot的4种死信队列处理方式
java·spring boot·后端
互联网全栈架构2 小时前
遨游Spring AI:第一盘菜Hello World
java·人工智能·后端·spring
coderSong25687 小时前
Java高级 |【实验八】springboot 使用Websocket
java·spring boot·后端·websocket
Mr_Air_Boy8 小时前
SpringBoot使用dynamic配置多数据源时使用@Transactional事务在非primary的数据源上遇到的问题
java·spring boot·后端
咖啡啡不加糖9 小时前
Redis大key产生、排查与优化实践
java·数据库·redis·后端·缓存
大鸡腿同学9 小时前
纳瓦尔宝典
后端
2302_8097983211 小时前
【JavaWeb】Docker项目部署
java·运维·后端·青少年编程·docker·容器
zhojiew11 小时前
关于akka官方quickstart示例程序(scala)的记录
后端·scala