当前位置: 面试刷题>> 什么是 Java 的 CountDownLatch?


在Java并发编程领域,CountDownLatch 是一个非常有用的同步工具类,它位于 java.util.concurrent 包下。作为高级程序员,理解并熟练使用 CountDownLatch 对于编写高效、可维护的并发程序至关重要。CountDownLatch 允许一个或多个线程等待其他线程完成一系列操作后再继续执行。简而言之,它是一个倒计时锁存器,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

基本原理

CountDownLatch 初始化时需要一个计数器(count),该计数器表示需要等待的事件数量。每次调用 countDown() 方法时,计数器会减一。当计数器到达零时,所有等待(通过 await() 方法)的线程都会被释放,继续执行。

应用场景

CountDownLatch 非常适用于以下场景:

  1. 并行计算:当需要将一个大任务分割成多个小任务并行处理,并等待所有小任务完成后再继续执行后续操作时。
  2. 资源初始化:在多个线程需要访问某些资源之前,这些资源需要被初始化完成。
  3. 性能测试:在测试多个并发执行的操作时,等待所有操作都启动后再进行统计或验证。

示例代码

假设我们有一个场景,需要同时启动多个线程去加载资源,待所有资源加载完毕后,再进行下一步处理。这里我们使用 CountDownLatch 来实现:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class CountDownLatchExample {

    public static void main(String[] args) throws InterruptedException {
        // 假设有5个资源需要加载
        int numberOfResources = 5;
        // 初始化CountDownLatch,计数器为5
        CountDownLatch latch = new CountDownLatch(numberOfResources);

        // 创建线程池
        ExecutorService executor = Executors.newFixedThreadPool(numberOfResources);

        // 提交5个任务去加载资源
        for (int i = 0; i < numberOfResources; i++) {
            final int resourceId = i;
            executor.submit(() -> {
                System.out.println(Thread.currentThread().getName() + " 正在加载资源 " + resourceId);
                // 模拟资源加载耗时
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                // 资源加载完成,计数器减一
                latch.countDown();
            });
        }

        // 等待所有资源加载完成
        latch.await();
        System.out.println("所有资源加载完成,继续后续操作...");

        // 关闭线程池
        executor.shutdown();
    }
}

在这个示例中,我们创建了一个 CountDownLatch 实例,其计数器初始化为5,表示有5个资源需要加载。我们提交了5个任务到线程池中,每个任务加载一个资源,并在加载完成后调用 countDown() 方法。主线程通过调用 await() 方法等待,直到所有资源都被加载完成(即计数器减至0)。此时,主线程继续执行后续操作。

注意事项

  • 避免死锁:确保在调用 await() 方法的线程中,不会持有其他锁,因为 await() 会导致线程阻塞,可能导致死锁。
  • 异常处理await() 方法会抛出 InterruptedException,需要妥善处理。通常的做法是重新设置中断状态,并可能根据需求中断当前操作或退出循环。
  • 性能考虑:在大量线程并发使用时,CountDownLatch 的性能可能受到影响。考虑使用其他并发工具,如 CyclicBarrierSemaphore,根据具体场景选择最合适的工具。

通过理解并熟练使用 CountDownLatch,你可以编写出更加高效、灵活的并发程序,从而在码小课等平台上分享你的编程经验和知识。

推荐面试题