跳转到内容

CountDownLatch使用

来自代码酷

CountDownLatch使用[编辑 | 编辑源代码]

介绍[编辑 | 编辑源代码]

CountDownLatch 是 Java 并发编程中的一个同步辅助类,允许一个或多个线程等待其他线程完成操作后再继续执行。它通过一个计数器实现,计数器的初始值由构造函数指定,线程调用 `countDown()` 方法减少计数器,而调用 `await()` 方法的线程会阻塞,直到计数器减至零。

CountDownLatch 适用于以下场景:

  • 主线程等待多个子线程完成初始化任务后再继续执行。
  • 多个线程并行执行任务,主线程等待所有任务完成后再汇总结果。
  • 模拟并发测试,确保所有线程同时启动。

核心方法[编辑 | 编辑源代码]

CountDownLatch 的核心方法包括:

  • `CountDownLatch(int count)`:构造函数,指定初始计数值。
  • `void await()`:阻塞当前线程,直到计数器减至零。
  • `boolean await(long timeout, TimeUnit unit)`:带超时的 `await()`,在指定时间内等待计数器归零。
  • `void countDown()`:计数器减一,如果计数器为零,则释放所有等待线程。
  • `long getCount()`:返回当前计数器的值。

代码示例[编辑 | 编辑源代码]

以下是一个简单的示例,展示如何使用 CountDownLatch 等待多个线程完成任务:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        final int THREAD_COUNT = 3;
        CountDownLatch latch = new CountDownLatch(THREAD_COUNT);

        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() -> {
                System.out.println("线程 " + Thread.currentThread().getName() + " 开始执行任务");
                try {
                    Thread.sleep(1000); // 模拟任务执行时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程 " + Thread.currentThread().getName() + " 完成任务");
                latch.countDown(); // 计数器减一
            }).start();
        }

        System.out.println("主线程等待所有子线程完成任务...");
        latch.await(); // 阻塞直到计数器归零
        System.out.println("所有子线程完成任务,主线程继续执行");
    }
}

输出示例:

线程 Thread-0 开始执行任务
线程 Thread-1 开始执行任务
线程 Thread-2 开始执行任务
主线程等待所有子线程完成任务...
线程 Thread-0 完成任务
线程 Thread-1 完成任务
线程 Thread-2 完成任务
所有子线程完成任务,主线程继续执行

解释: 1. 主线程创建 `CountDownLatch`,初始值为 3(对应 3 个子线程)。 2. 每个子线程完成任务后调用 `countDown()`,计数器减一。 3. 主线程调用 `await()` 阻塞,直到计数器归零,然后继续执行。

实际应用场景[编辑 | 编辑源代码]

场景 1:多线程初始化[编辑 | 编辑源代码]

在系统启动时,可能需要多个服务(如数据库连接、缓存加载)并行初始化,主线程等待所有服务就绪后再启动应用。

public class ServiceInitializer {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(2);

        new Thread(() -> {
            System.out.println("初始化数据库连接...");
            // 模拟初始化耗时
            try { Thread.sleep(2000); } catch (InterruptedException e) {}
            System.out.println("数据库连接就绪");
            latch.countDown();
        }).start();

        new Thread(() -> {
            System.out.println("加载缓存数据...");
            // 模拟加载耗时
            try { Thread.sleep(1500); } catch (InterruptedException e) {}
            System.out.println("缓存数据加载完成");
            latch.countDown();
        }).start();

        latch.await();
        System.out.println("所有服务初始化完成,启动应用");
    }
}

场景 2:并发测试[编辑 | 编辑源代码]

在性能测试中,确保所有线程同时开始执行任务:

public class ConcurrentTest {
    public static void main(String[] args) throws InterruptedException {
        final int THREAD_COUNT = 5;
        CountDownLatch startSignal = new CountDownLatch(1);
        CountDownLatch doneSignal = new CountDownLatch(THREAD_COUNT);

        for (int i = 0; i < THREAD_COUNT; i++) {
            new Thread(() -> {
                try {
                    startSignal.await(); // 等待开始信号
                    System.out.println("线程 " + Thread.currentThread().getName() + " 执行任务");
                    doneSignal.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }

        System.out.println("准备开始并发测试...");
        Thread.sleep(1000); // 模拟准备时间
        startSignal.countDown(); // 释放所有线程
        doneSignal.await(); // 等待所有线程完成
        System.out.println("测试结束");
    }
}

与 CyclicBarrier 的区别[编辑 | 编辑源代码]

CountDownLatch 和 `CyclicBarrier` 都用于线程同步,但有以下区别:

  • 计数器重置:CountDownLatch 的计数器不可重置,而 CyclicBarrier 可以重复使用。
  • 线程角色:CountDownLatch 的线程分为等待线程和计数线程,而 CyclicBarrier 的线程互相等待。
  • 用途:CountDownLatch 用于一个线程等待多个线程,CyclicBarrier 用于多个线程互相等待。

注意事项[编辑 | 编辑源代码]

1. 计数器必须为正整数,否则抛出 `IllegalArgumentException`。 2. `await()` 方法可能被中断,需处理 `InterruptedException`。 3. 避免过度使用 CountDownLatch,在复杂场景中考虑 `CompletableFuture` 或 `Phaser`。

总结[编辑 | 编辑源代码]

CountDownLatch 是 Java 并发编程中强大的同步工具,适用于主线程等待多个子线程完成的场景。通过计数器机制,它简化了线程间的协调工作。理解其原理和适用场景,能有效提升多线程程序的可靠性和性能。