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

其他使用场景

  • 统计大屏页面数据时,把各个模块的数据使用多线程统计出来后,封装之后再一起返给前端。
  • 文件处理时,同时启动多个线程分别处理不同的文件,多线程把所有文件处理完毕后,再进行汇总和分析。
  • 系统启动时多线程加载配置文件、初始化数据库连接等操作,当这些操作完成后,业务处理线程才能开始工作。
相关推荐
2402_8575893619 分钟前
Spring Boot新闻推荐系统设计与实现
java·spring boot·后端
J老熊28 分钟前
Spring Cloud Netflix Eureka 注册中心讲解和案例示范
java·后端·spring·spring cloud·面试·eureka·系统架构
Benaso31 分钟前
Rust 快速入门(一)
开发语言·后端·rust
sco528231 分钟前
SpringBoot 集成 Ehcache 实现本地缓存
java·spring boot·后端
原机小子1 小时前
在线教育的未来:SpringBoot技术实现
java·spring boot·后端
吾日三省吾码1 小时前
详解JVM类加载机制
后端
努力的布布1 小时前
SpringMVC源码-AbstractHandlerMethodMapping处理器映射器将@Controller修饰类方法存储到处理器映射器
java·后端·spring
PacosonSWJTU1 小时前
spring揭秘25-springmvc03-其他组件(文件上传+拦截器+处理器适配器+异常统一处理)
java·后端·springmvc
记得开心一点嘛2 小时前
在Java项目中如何使用Scala实现尾递归优化来解决爆栈问题
开发语言·后端·scala
黄俊懿2 小时前
【深入理解SpringCloud微服务】手写实现各种限流算法——固定时间窗、滑动时间窗、令牌桶算法、漏桶算法
java·后端·算法·spring cloud·微服务·架构