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