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);


目录
相关文章
|
6月前
|
设计模式 Java
CountDownLatch和CyclicBarrier源码详解
我现在有个场景:现在我有50个任务,这50个任务在完成之后,才能执行下一个函数,要是你,你怎么设计?可以用JDK给我们提供的线程工具类,CountDownLatch和CyclicBarrier都可以完成这个需求。基于AQS实现,会将构造CountDownLatch的入参传递至statecountDown()就是在利用CAS将state减1,await)实际就是让头节点一直在等待state为0时,释放所有等待的线程。
69 1
|
3月前
|
消息中间件 RocketMQ
CountDownLatch原理
文章讲述了CountDownLatch的工作原理及其用途: 1. CountDownLatch是一个简单的同步工具,用于等待一组操作完成。 2. 它通过AQS框架实现,利用共享锁模式控制线程的等待与唤醒。 3. 适用于多线程环境下,一个线程需要等待多个其他线程完成各自的任务后再继续执行的场景。
|
6月前
并发编程之CountDownLatch和CyclicBarrier的详细解析(带小案例)
并发编程之CountDownLatch和CyclicBarrier的详细解析(带小案例)
80 0
|
6月前
学习多线程之CountDownLatch使用
学习多线程之CountDownLatch使用
52 0
|
Java
第二季:6CountDownLatch/CyclicBarrier/Semaphore使用过吗?【Java面试题】
第二季:6CountDownLatch/CyclicBarrier/Semaphore使用过吗?【Java面试题】
36 0
|
Java API
Java并发之CountDownLatch
Java并发之CountDownLatch
172 1
Java并发之CountDownLatch
|
安全 Java 测试技术
CountDownLatch原理剖析
CountDownLatch原理剖析
108 0
|
Java
Java多线程-CountDownLatch、Semaphone、CyclicBarrier入门
多线程CountDownLatch、Semaphone、CyclicBarrier讲解
181 1
Java多线程-CountDownLatch、Semaphone、CyclicBarrier入门
|
安全 Java 调度
JUC并发编程学习(八)-CountDownLatch、CyclicBarrier、Semaphore的使用
JUC并发编程学习(八)-CountDownLatch、CyclicBarrier、Semaphore的使用
JUC并发编程学习(八)-CountDownLatch、CyclicBarrier、Semaphore的使用