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


目录
相关文章
|
4天前
|
设计模式 Java
CountDownLatch和CyclicBarrier源码详解
我现在有个场景:现在我有50个任务,这50个任务在完成之后,才能执行下一个函数,要是你,你怎么设计?可以用JDK给我们提供的线程工具类,CountDownLatch和CyclicBarrier都可以完成这个需求。基于AQS实现,会将构造CountDownLatch的入参传递至statecountDown()就是在利用CAS将state减1,await)实际就是让头节点一直在等待state为0时,释放所有等待的线程。
43 1
|
4天前
并发编程之CountDownLatch和CyclicBarrier的详细解析(带小案例)
并发编程之CountDownLatch和CyclicBarrier的详细解析(带小案例)
11 0
|
4天前
|
Java 数据库连接 mybatis
CountDownLatch(倒计时器)源码解读与使用
CountDownLatch(倒计时器)源码解读与使用
|
安全 Java 测试技术
CountDownLatch原理剖析
CountDownLatch原理剖析
|
Java API
Java并发之CountDownLatch
Java并发之CountDownLatch
134 1
Java并发之CountDownLatch
|
安全 Java 调度
JUC并发编程学习(八)-CountDownLatch、CyclicBarrier、Semaphore的使用
JUC并发编程学习(八)-CountDownLatch、CyclicBarrier、Semaphore的使用
JUC并发编程学习(八)-CountDownLatch、CyclicBarrier、Semaphore的使用
【JAVA并发编程专题】CountDownLatch的理解与使用
【JAVA并发编程专题】CountDownLatch的理解与使用
|
Java
Java多线程-CountDownLatch、Semaphone、CyclicBarrier入门
多线程CountDownLatch、Semaphone、CyclicBarrier讲解
157 1
Java多线程-CountDownLatch、Semaphone、CyclicBarrier入门