1. 定义

CountDownLatch 是 Java 并发包 java.util.concurrent 中的一个同步工具类。它允许一个或多个线程等待其他线程完成操作。CountDownLatch 内部维护了一个计数器,该计数器的初始值在创建 CountDownLatch 对象时指定,之后线程可以通过调用 countDown() 方法来减少计数器的值,当计数器的值变为 0 时,所有等待的线程将被释放继续执行。

2. 作用

  • 线程同步:可以让一个或多个线程等待其他一组线程完成特定操作后再继续执行。比如,主线程需要等待多个子线程完成数据加载后再进行后续处理。
  • 并行任务协调:在并行执行多个任务时,确保所有任务都完成后再进行下一步操作。例如,一个大型计算任务被拆分成多个子任务并行执行,主线程需要等待所有子任务完成后再汇总结果。

3. 使用方法

CountDownLatch 的使用主要涉及以下几个步骤:

  • 创建 CountDownLatch 对象:在创建时需要指定计数器的初始值,这个值通常表示需要等待完成的操作数量。
  • 线程执行任务:在每个需要完成的任务线程中,当任务完成时调用 countDown() 方法来减少计数器的值。
  • 等待计数器归零:需要等待的线程调用 await() 方法,该方法会阻塞线程,直到计数器的值变为 0。

以下是一个简单的示例代码,展示了 CountDownLatch 的使用:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) {
        int taskCount = 3;
        // 创建 CountDownLatch 对象,初始计数器值为 taskCount
        CountDownLatch latch = new CountDownLatch(taskCount);

        // 创建并启动多个任务线程
        for (int i = 0; i < taskCount; i++) {
            final int taskId = i;
            new Thread(() -> {
                try {
                    System.out.println("Task " + taskId + " is starting...");
                    // 模拟任务执行
                    Thread.sleep((long) (Math.random() * 1000));
                    System.out.println("Task " + taskId + " is completed.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    // 任务完成,计数器减 1
                    latch.countDown();
                }
            }).start();
        }

        try {
            System.out.println("Main thread is waiting for all tasks to complete...");
            // 主线程等待,直到计数器为 0
            latch.await();
            System.out.println("All tasks are completed. Main thread can continue.");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这个示例中:

  • 首先创建了一个 CountDownLatch 对象,初始计数器值为 3,表示有 3 个任务需要完成。
  • 然后创建并启动了 3 个线程来模拟任务执行,每个线程在任务完成后调用 countDown() 方法。
  • 主线程调用 await() 方法等待,直到所有任务完成(计数器变为 0),然后继续执行后续代码。