CountDownLatch
是 Java 并发包(
java.util.concurrent
)中的一个同步辅助类,它允许一个或多个线程等待一组操作完成。
CountDownLatch
是基于 AQS(AbstractQueuedSynchronizer)实现的。其核心思想是
维护一个倒计数
,每次倒计数减少到零时,等待的线程才会继续执行。它的主要设计目标是允许多个线程协调完成一组任务。
public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
构造
CountDownLatch
时传入的
count
决定了计数器的初始值。该计数器控制了线程的释放。
AQS 是
CountDownLatch
的基础,通过自定义内部类
Sync
实现,
Sync
继承了 AQS 并提供了必要的方法。以下是关键操作:
acquireShared(int arg)
releaseShared(int arg)
countDown()
countDown()
releaseShared()
await()
acquireSharedInterruptibly(1)
CountDownLatch
的核心是基于
AbstractQueuedSynchronizer
(AQS)来管理计数器状态的。AQS 是 JUC 中许多同步工具的基础,通过一个独占/共享模式的同步队列实现线程的管理和调度。
CountDownLatch
采用 AQS 的
共享锁机制
来控制多个线程等待一个条件。
AQS 设计了两种同步模式:
独占模式
(exclusive)和
共享模式
(shared)。
CountDownLatch
使用共享模式:
ReentrantLock
Semaphore
CountDownLatch
CountDownLatch
的
await()
和
countDown()
方法对应于 AQS 的
acquireShared()
和
releaseShared()
操作。
acquireShared()
会检查同步状态(计数器值),若状态为零则立即返回,否则阻塞当前线程,进入等待队列。
releaseShared()
用于减少计数器并唤醒所有等待线程。
CountDownLatch
通过一个私有的内部类
Sync
来实现同步逻辑。
Sync
继承自
AQS
,并重写
tryAcquireShared(int arg)
和
tryReleaseShared(int arg)
方法。
static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
setState(count);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// 自旋减计数器
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
countDown()
true
tryReleaseShared
方法使用 CAS(compare-and-set)更新计数器,避免了锁的开销。CAS 操作由 CPU 原语(如
cmpxchg
指令)支持,实现了高效的非阻塞操作。这种设计保证了
countDown()
的线程安全性,使得多个线程能够并发地减少计数器。
CountDownLatch
不支持复用,因为 AQS 的
ConditionObject
被设计为单一触发模式。计数器一旦降至零,
CountDownLatch
无法重置,只能释放所有线程,而不能再次设置初始计数器值。这就是其不可复用的根本原因。
CountDownLatch
CountDownLatch
以下示例展示如何使用
CountDownLatch
实现一个并发任务等待所有子任务完成的机制。
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
private static final int TASK_COUNT = 5;
private static CountDownLatch latch = new CountDownLatch(TASK_COUNT);
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < TASK_COUNT; i++) {
new Thread(new Task(i + 1, latch)).start();
}
// 主线程等待所有任务完成
latch.await();
System.out.println("所有任务已完成,继续主线程任务");
}
static class Task implements Runnable {
private final int taskNumber;
private final CountDownLatch latch;
Task(int taskNumber, CountDownLatch latch) {
this.taskNumber = taskNumber;
this.latch = latch;
}
@Override
public void run() {
try {
System.out.println("子任务 " + taskNumber + " 开始执行");
Thread.sleep((int) (Math.random() * 1000)); // 模拟任务执行时间
System.out.println("子任务 " + taskNumber + " 完成");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown(); // 完成一个任务,计数器减一
}
}
}
}
原理和用途
:
CyclicBarrier
底层实现
:
CyclicBarrier
ReentrantLock
Condition
与 CountDownLatch 的对比
:
CyclicBarrier
CountDownLatch
CountDownLatch
countDown()
CyclicBarrier
原理和用途
:
Semaphore
底层实现
:
Semaphore
CountDownLatch
与 CountDownLatch 的对比
:
Semaphore
CountDownLatch
Semaphore
CountDownLatch
原理和用途
:
Phaser
CyclicBarrier
底层实现
:
Phaser
与 CountDownLatch 的对比
:
Phaser
CountDownLatch
Phaser
CountDownLatch
CountDownLatch
是一个轻量级、不可复用的倒计数同步器,适合简单的一次性线程协调。其基于 AQS 的共享锁实现使得线程等待和计数器更新具有高效的并发性。虽然
CountDownLatch
不具备重用性,但其设计简洁,尤其适合需要等待多线程任务完成的场景。
与其他 JUC 工具相比:
CyclicBarrier
Semaphore
Phaser
选择适合的同步工具,取决于任务的性质、线程参与动态性以及是否需要重用同步控制。