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
所有部件加工完成,可以组装电脑

其他使用场景

  • 统计大屏页面数据时,把各个模块的数据使用多线程统计出来后,封装之后再一起返给前端。
  • 文件处理时,同时启动多个线程分别处理不同的文件,多线程把所有文件处理完毕后,再进行汇总和分析。
  • 系统启动时多线程加载配置文件、初始化数据库连接等操作,当这些操作完成后,业务处理线程才能开始工作。
相关推荐
许野平1 小时前
Rust: 利用 chrono 库实现日期和字符串互相转换
开发语言·后端·rust·字符串·转换·日期·chrono
齐 飞3 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb
LunarCod3 小时前
WorkFlow源码剖析——Communicator之TCPServer(中)
后端·workflow·c/c++·网络框架·源码剖析·高性能高并发
码农派大星。4 小时前
Spring Boot 配置文件
java·spring boot·后端
杜杜的man5 小时前
【go从零单排】go中的结构体struct和method
开发语言·后端·golang
幼儿园老大*5 小时前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
llllinuuu5 小时前
Go语言结构体、方法与接口
开发语言·后端·golang
cookies_s_s5 小时前
Golang--协程和管道
开发语言·后端·golang
为什么这亚子5 小时前
九、Go语言快速入门之map
运维·开发语言·后端·算法·云原生·golang·云计算
想进大厂的小王5 小时前
项目架构介绍以及Spring cloud、redis、mq 等组件的基本认识
redis·分布式·后端·spring cloud·微服务·架构