Java 入门指南:Java 并发编程 —— 同步工具类 CountDownLatch(倒计时门闩)

同步工具类

JUC(Java.util.concurrent)是 Java 提供的用于并发编程的工具类库,其中包含了一些通信工具类,用于在多个线程之间进行协调和通信,特别是在多线程和网络通信方面。这些工具类提供了丰富的功能,帮助开发者高效地实现复杂的并发控制和网络通信需求。

![[JUC Communication Utilities.png]]

CountDownLatch

CountDownLatch(倒计时门闩)是 Java 中的一种同步辅助类,用于控制多个线程之间的协调,允许一个或多个线程等待其他线程完成一组操作

CountDownLatch 通过维护一个计数器来实现,计数器初始值可以设定为一个正整数(通常为需要等待的线程数量或任务数量)。每当一个线程完成任务后,计数器的值就会减1。当计数器的值减到 0 时,所有等待的线程都会被释放,此时所有因调用 await() 方法而阻塞的线程都会被唤醒,继续执行后续的操作。

常用方法

CountDownLatch 提供了几个关键的方法来实现其功能:

  • CountDownLatch(int count):构造函数,用于创建 CountDownLatch 实例,并设置计数器的初始值。

  • void countDown():递减计数器的值。每当一个线程完成任务后,可以调用此方法将计数器的值减1。

  • void await():使当前线程在计数器减至 0 之前一直等待。如果线程在等待过程中被中断,则会抛出 InterruptedException 异常。

  • boolean await(long timeout, TimeUnit unit):使当前线程在计数器减至 0 之前等待指定的时间。如果在等待时间内计数器减至 0,则返回 true;如果等待时间超时而计数器仍未减至 0,则返回 false。在等待过程中,如果线程被中断,则会抛出 InterruptedException 异常。

  • long getCount():获取当前计数器的值。这个方法主要用于调试或测试目的。

使用步骤

使用 CountDownLatch 的基本步骤如下:

  1. 创建一个 CountDownLatch 对象,并指定计数器的初始值。

  2. 在需要等待的线程中,调用 countDown() 方法来减少计数器的值。

  3. 在等待其他线程的线程中,调用 await() 方法,等待计数器的值减到0。

当计数器的值减到 0 时,所有等待的线程会被唤醒,可以继续执行后续操作。

需要注意的是,CountDownLatch一次性的,一旦计数器的值减到 0,就不能重置。如果需要可重复使用的类似功能,可以考虑使用其他同步工具类如 CyclicBarrierPhaser

CountDownLatch 在多线程协调和同步的场景中非常有用,可以有效地控制线程的执行顺序和状态的正确性。

适用场景

CountDownLatch 的使用场景是一个或多个线程需要等待其他线程完成某个操作后,再继续执行下去。它常用于以下情况:

  1. 并行计算的启动屏障:在并行计算中,可能需要等待所有并行任务都准备好后再开始执行。此时,可以将 CountDownLatch 的初始值设置为并行任务的数量,每个任务在开始执行前都调用 countDown() 方法,而主线程则调用 await() 方法等待所有任务都准备就绪。

  2. 等待多个线程完成:当有多个线程正在执行,并且需要等待所有这些线程都完成后再继续执行其他操作时,可以使用 CountDownLatch。通过将计数器的初始值设置为线程的数量,并在每个线程执行完毕后调用 countDown() 方法,主线程可以调用 await() 方法等待所有线程完成。

  3. 并发测试:在进行并发测试时,可能需要模拟多个线程同时触发某个操作。此时,可以使用 CountDownLatch 来等待所有线程都准备好后再一起触发操作。

使用示例

以下是一个简单的示例代码,演示了如何使用 CountDownLatch 来等待多个线程完成:

import java.util.concurrent.CountDownLatch;  
  
public class CountDownLatchExample {  
    public static void main(String[] args) throws InterruptedException {  
        int numberOfThreads = 5;  
        CountDownLatch latch = new CountDownLatch(numberOfThreads);  
  
        for (int i = 0; i < numberOfThreads; i++) {  
            final int threadNum = i;  
            new Thread(() -> {  
                try {  
                    // 模拟任务执行  
                    Thread.sleep(1000);  
                    System.out.println("Thread " + threadNum + " is done.");  
                } catch (InterruptedException e) {  
                    Thread.currentThread().interrupt();  
                } finally {  
                    latch.countDown();  
                }  
            }).start();  
        }  
  
        // 等待所有线程完成  
        latch.await();  
        System.out.println("All threads have completed.");  
    }  
}

在这个示例中,我们创建了一个包含5个线程的线程池(尽管这里直接使用了 Thread 类来创建线程,但原理相同),每个线程都执行一个模拟任务(通过 Thread.sleep(1000) 实现)。每个线程在完成任务后都会调用 latch.countDown() 来减少计数器的值。主线程调用 latch.await() 等待,直到计数器的值减到 0,即所有线程都完成了任务。然后,主线程继续执行并打印出所有线程都已完成的消息。

  • 13
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值