CountDownLatch 实战

简介: CountDownLatch 实战

CountDownLatch 是一个同步辅助工具,它允许一个或多个线程等待其他线程完成一系列操作。以下是一个 CountDownLatch 的实战示例,展示了如何使用它来协调多个线程的执行顺序。

CountDownLatch的主要功能是,它有一个计数器,初始值为一个非负整数。当计数器的值减为0时,所有等待的线程才会继续执行。

原理

CountDownLatch是一个同步协助类,它允许一个或多个线程等待,直到其他线程完成操作集。它使用给定的计数值(count)初始化,通过调用await方法,线程会进入等待状态,直到计数值由于countDown方法的调用达到0,此时所有等待的线程才会被释放,继续执行后续操作。

CountDownLatch的实现原理是基于锁和计数器。它使用一个内部锁来控制对计数器的访问,同时维护一个计数器来记录给定的计数值。初始化时,计数器的值等于给定的计数值。每次调用countDown方法时,计数器会减1,而每次调用await方法时,计数器会加1。当计数器的值为0时,表示所有线程都已完成操作,await方法会释放锁并返回。

由于CountDownLatch是基于锁和计数器实现的,因此它具有线程安全性。同时,它也支持多个线程之间的协作和同步,使得线程可以等待其他线程完成操作,这对于并发编程来说非常有用。

使用

CountDownLatch的常用方法有:

CountDownLatch(int count): 创建一个计数器,初始值为给定的非负整数count。

void await() throws InterruptedException: 使当前线程等待,直到计数器减为0或者当前线程被中断。

boolean await(long timeout, TimeUnit unit) throws InterruptedException: 使当前线程等待,直到计数器减为0,或者当前线程被中断,或者等待时间超过指定的timeout。

void countDown(): 计数器减1。

CountDownLatch的使用场景包括:

等待一组线程执行完毕:通过初始化一个计数器,等待指定数量的任务完成后再执行后续任务。

实现一个线程释放一组线程的功能:通过一个计数器来控制多个线程的执行顺序,当计数器减到0时,所有线程继续执行。

多个线程释放多个线程的场景:通过CountDownLatch可以控制多个线程的执行顺序,从而实现多个线程的协调和同步。

使用CountDownLatch时需要注意以下几点:

CountDownLatch不能被重复使用,一旦计数器减到0,就不能再被await()了。

如果等待的线程被中断,CountDownLatch不会抛出异常,而是会抛出InterruptedException。

如果在指定的超时时间内,计数器没有减到0,await()方法会返回false。

总之,CountDownLatch是一个非常实用的同步工具类,可以用于实现多线程的协调和同步。

例子

假设我们有一个程序,其中有两个线程需要按照一定的顺序执行:线程 A 需要在所有线程 B 之前执行完毕。我们可以使用 CountDownLatch 来实现这个顺序。

import java.util.concurrent.CountDownLatch;  
  
public class CountDownLatchExample {  
  
    public static void main(String[] args) throws InterruptedException {  
        CountDownLatch latch = new CountDownLatch(5);  
  
        // 创建并启动线程 B  
        for (int i = 0; i < 5; i++) {  
            new Thread(() -> {  
                try {  
                    System.out.println("线程B开始执行...");  
                    Thread.sleep(1000); // 模拟线程B的执行时间  
                    System.out.println("线程B执行完毕...");  
                } catch (InterruptedException e) {  
                    e.printStackTrace();  
                } finally {  
                    latch.countDown(); // 减少计数  
                }  
            }).start();  
        }  
  
        // 等待所有线程 B 执行完毕  
        latch.await();  
  
        // 创建并启动线程 A  
        new Thread(() -> {  
            System.out.println("线程A开始执行...");  
            // 模拟线程A的执行时间  
            try {  
                Thread.sleep(3000);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
            System.out.println("线程A执行完毕...");  
        }).start();  
    }  
}

在上述代码中,我们首先创建了一个 CountDownLatch 对象,并指定初始计数值为 5。然后,我们创建了 5 个线程 B,每个线程在执行完成后会调用 countDown() 方法减少计数器的值。接着,我们使用latch.await() 方法等待所有线程 B 执行完毕。最后,我们创建了一个线程 A,该线程在所有线程 B 执行完毕后开始执行。

通过使用 CountDownLatch,我们可以确保线程 A 只在所有线程 B 执行完毕后才开始执行,实现了线程之间的顺序控制。

实战代码

ExecutorService executorService  = newFixedThreadPool(3);
        CountDownLatch countDownLatch = new CountDownLatch(3);
        List<TestDefectUpdateVo> listNo=new ArrayList<>();
        List<TestDefectUpdateVo> testDefectUpdateVos=new ArrayList<>();
        List<TestDefectUpdateVo> testDefectUpdateVosDic=new ArrayList<>();
        executorService.submit(()->{
            List<TestDefectUpdateVo> testList = testDefectMapper.selectProjectNum(leafProjectId, null);
            listNo.addAll(testList);
            countDownLatch.countDown();
        }).get();
        executorService.submit(()->{
            List<TestDefectUpdateVo> testDefect = testDefectMapper.selectProjectAllNum(leafProjectId);
            testDefectUpdateVos.addAll(testDefect);
            countDownLatch.countDown();
        }).get();
        executorService.submit(()->{
            List<TestDefectUpdateVo> testDefectUpdate = testDefectMapper.selectProjectDicNum(leafProjectId);
            testDefectUpdateVosDic.addAll(testDefectUpdate);
            countDownLatch.countDown();
        }).get();

        countDownLatch.await(5, TimeUnit.SECONDS);


目录
相关文章
Element UI - el-scrollbar 如何隐藏横向滚动条?
Element UI - el-scrollbar 如何隐藏横向滚动条?
931 0
|
边缘计算 安全 物联网
探索边缘计算:架构、优势与未来趋势
探索边缘计算:架构、优势与未来趋势
|
机器学习/深度学习 人工智能 算法
人工智能中的知识表示与推理
人工智能中的知识表示与推理
783 1
|
C# 开发工具 Windows
C# 获取Windows系统信息以及CPU、内存和磁盘使用情况
C# 获取Windows系统信息以及CPU、内存和磁盘使用情况
466 0
|
人工智能 自然语言处理
WebDreamer:基于大语言模型模拟网页交互增强网络规划能力的框架
WebDreamer是一个基于大型语言模型(LLMs)的网络智能体框架,通过模拟网页交互来增强网络规划能力。它利用GPT-4o作为世界模型,预测用户行为及其结果,优化决策过程,提高性能和安全性。WebDreamer的核心在于“做梦”概念,即在实际采取行动前,用LLM预测每个可能步骤的结果,并选择最有可能实现目标的行动。
312 1
WebDreamer:基于大语言模型模拟网页交互增强网络规划能力的框架
|
缓存 监控 Java
如何运用JAVA开发API接口?
本文详细介绍了如何使用Java开发API接口,涵盖创建、实现、测试和部署接口的关键步骤。同时,讨论了接口的安全性设计和设计原则,帮助开发者构建高效、安全、易于维护的API接口。
1066 4
|
Android开发
Android面试之Activity启动流程简述
Android面试之Activity启动流程简述
247 6
|
JSON 安全 API
实战指南:使用PHP构建高性能API接口服务端
构建RESTful API的简要指南:使用PHP和Laravel,先安装Laravel并配置数据库,接着在`api.php`中定义资源路由,创建`PostController`处理CRUD操作,定义`Post`模型与数据库交互。使用Postman测试API功能,如创建文章。别忘了关注安全性、错误处理和性能优化。
453 2
|
存储 分布式计算 监控
Spark中广播变量
【8月更文挑战第13天】
555 0