首页 > 编程笔记 > Java笔记 阅读:10

Java CountDownLatch的用法(附带实例)

CountDownLatch 是 JUC(java.util.concurrent 包)中的一个同步工具类,用来协调多个线程之间的同步,它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成之后,再继续执行。

CountDownLatch 利用一个计数器进行实现,计数器的初始值就是线程的数量,当每个被计数的线程完成任务后,计数器的值减 1,当计数器的值为 0 时,表示所有线程都已经完成了任务,然后等待的线程就可以恢复执行。

下面是使用 CountDownLatch 的主要步骤:
下面是一个 CountDownLatch 使用示例,示例代码如下:
import java.util.concurrent.CountDownLatch;
public class Main {
    public static void main(String[] args) {
        // 假设我们需要等待3个并行线程任务
        final CountDownLatch latch = new CountDownLatch(3);
        // 创建任务并启动线程
        for (int i = 1; i <= 3; i++) {
            final int taskId = i;
            new Thread(() -> {
                try {
                    // 模拟任务执行
                    System.out.println("任务 " + taskId + " 正在执行");
                    Thread.sleep((long) (Math.random() * 1000 + 500));
                    System.out.println("任务 " + taskId + " 执行完毕");
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                } finally {
                    // 任务完成后,计数减少
                    latch.countDown();
                }
            }).start();
        }
        try {
            // 主线程等待所有任务完成
            System.out.println("主线程等待任务完成……");
            latch.await();
            System.out.println("所有任务已完成,主线程继续执行……");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
在上述示例中,我们创建了 3 个并行线程任务,每个任务完成后都会调用 countDown() 方法。主线程在调用 await() 后会等待,直到 3 个任务都调用了countDown()方法,计数器的值变为0后,主线程才继续执行。这个机制在许多企业级应用中非常有用,例如,在初始化过程中,主线程需要等待多个服务启动完成后才能继续执行。通过使用 CountDownLatch 可以简化线程之间的协调。

CountDownLatch 主要通过一个计数器实现。此外,CountDownLatch 还使用了 AQS 作为同步控制的基础框架。AQS 是用来实现锁或其他同步器的一个基础类,提供了一个 FIFO 队列,可以用来管理等待的线程。

下面我们看一下 CountDownLatch 的实现源码,核心实现如下:
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
public class CountDownLatch {
    // 继承AQS提供的同步器
    private static final class Sync extends AbstractQueuedSynchronizer {
        Sync(int count) {
            setState(count);
        }
        int getCount() {
            return getState();
        }
        // 减少计数器的值,当计数器的值变为0时释放所有等待的线程
        public boolean tryReleaseShared(int releases) {
            // 无限循环,使用CAS操作保证状态的正确设置
            for (;;) {
                int c = getState();
                if (c == 0) // 如果计数器的值已经为0,返回false
                    return false;
                int nextc = c - 1;
                if (compareAndSetState(c, nextc)) // 如果CAS操作成功,则递减计数器的值
                    return nextc == 0; // 如果计数器的下一个值是0,表示可以释放共享锁
            }
        }
        // 当计数器的值为0时,获取共享锁
        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }
    }
    private final Sync sync;
    // 初始化CountDownLatch
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
    // 减少计数器的值
    public void countDown() {
        sync.releaseShared(1);
    }
    // 等待计数器的值变为0
    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
}
在上述代码中,Sync 是 CountDownLatch 的一个静态内部类,它扩展了  Abstract-QueuedSynchronizer 类,维护着 state 同步状态值(state 表示剩余需要等待的事件数量)。

CountDownLatch 为我们提供了 countDown() 和 await() 两个公有方法,内部调用了 Sync 的 tryReleaseShared() 和 tryAcquireShared(),它们分别负责让计数器的值减 1 和等待计数器的值变为 0:
总之,CountDownLatch 提供了清晰的等待/通知机制,易于理解和使用,能够简洁、高效地协调多个线程的执行顺序,保证一组线程都完成后才触发其他线程的执行,适用于资源加载、任务初始化等场景,是提升多线程程序性能和可靠性的重要工具。

相关文章