Java CountDownLatch的用法(附带实例)
CountDownLatch 是 JUC(java.util.concurrent 包)中的一个同步工具类,用来协调多个线程之间的同步,它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成之后,再继续执行。
CountDownLatch 利用一个计数器进行实现,计数器的初始值就是线程的数量,当每个被计数的线程完成任务后,计数器的值减 1,当计数器的值为 0 时,表示所有线程都已经完成了任务,然后等待的线程就可以恢复执行。
下面是使用 CountDownLatch 的主要步骤:
下面是一个 CountDownLatch 使用示例,示例代码如下:
CountDownLatch 主要通过一个计数器实现。此外,CountDownLatch 还使用了 AQS 作为同步控制的基础框架。AQS 是用来实现锁或其他同步器的一个基础类,提供了一个 FIFO 队列,可以用来管理等待的线程。
下面我们看一下 CountDownLatch 的实现源码,核心实现如下:
CountDownLatch 为我们提供了 countDown() 和 await() 两个公有方法,内部调用了 Sync 的 tryReleaseShared() 和 tryAcquireShared(),它们分别负责让计数器的值减 1 和等待计数器的值变为 0:
总之,CountDownLatch 提供了清晰的等待/通知机制,易于理解和使用,能够简洁、高效地协调多个线程的执行顺序,保证一组线程都完成后才触发其他线程的执行,适用于资源加载、任务初始化等场景,是提升多线程程序性能和可靠性的重要工具。
CountDownLatch 利用一个计数器进行实现,计数器的初始值就是线程的数量,当每个被计数的线程完成任务后,计数器的值减 1,当计数器的值为 0 时,表示所有线程都已经完成了任务,然后等待的线程就可以恢复执行。
下面是使用 CountDownLatch 的主要步骤:
- 初始化一个 CountDownLatch 的实例,指定计数器的初始值。
- 当需要等待事件完成时,可以调用 await() 方法阻塞当前线程,直到计数器的值变为 0。
- 当某个事件完成时,可以调用 countDown() 方法将计数器的值减 1。
- 当计数器的值变为0时,所有在 await() 方法上等待的线程将会被唤醒并继续执行。
下面是一个 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:
- tryReleaseShared() 是当事件发生时需要使用的方法,它会尝试通过 CAS 操作减少状态值,当状态值为 0 时允许释放锁;
- tryAcquireShared() 是线程尝试获取共享锁时调用的方法,它判断状态值是否为 0,如果为 0 表示锁可以被获取,否则获取失败。
总之,CountDownLatch 提供了清晰的等待/通知机制,易于理解和使用,能够简洁、高效地协调多个线程的执行顺序,保证一组线程都完成后才触发其他线程的执行,适用于资源加载、任务初始化等场景,是提升多线程程序性能和可靠性的重要工具。