CountDownLatch
使用给定的计数进行初始化。 await
方法会阻塞,直到由于调用 countDown()
方法而使当前计数达到零,此后所有等待的线程都会被释放,并且任何后续调用 await()
的方法都会立即返回。这是一个一次性现象 -- 计数无法重置。如果需要一个可以重置计数的版本,请考虑使用 CyclicBarrier
。
CountDownLatch
是一个多功能的同步工具,可以用于多种目的。使用计数为一的 CountDownLatch
可以作为一个简单的开关:所有调用 await()
的线程都会在开关处等待,直到被调用 countDown()
的线程打开。使用初始化为 N 的 CountDownLatch
可以使一个线程等待,直到 N 个线程完成某个动作,或某个动作已经完成了 N 次。
CountDownLatch
的一个有用属性是,调用 countDown
的线程不需要等待计数达到零才能继续,它只是阻止任何线程在所有线程都通过 await()
之前继续进行。
示例用法: 这里是一对类,其中一组工作线程使用了两个倒计时闩:
- 第一个是一个启动信号,防止任何工作线程在驱动程序准备好让它们继续之前进行;
- 第二个是一个完成信号,允许驱动程序等待直到所有工作线程都完成。
class Driver { // ...
void main() throws InterruptedException {
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i) // 创建并启动线程
new Thread(new Worker(startSignal, doneSignal)).start();
doSomethingElse(); // 先不要运行
startSignal.countDown(); // 让所有线程继续
doSomethingElse();
doneSignal.await(); // 等待所有线程完成
}
}
class Worker implements Runnable {
private final CountDownLatch startSignal;
private final CountDownLatch doneSignal;
Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
this.startSignal = startSignal;
this.doneSignal = doneSignal;
}
public void run() {
try {
startSignal.await();
doWork();
doneSignal.countDown();
} catch (InterruptedException ex) {} // 返回;
}
void doWork() { ... }
}
另一个典型的用法是将问题分解为 N 部分,用一个执行该部分并在闩上计数的 Runnable 来描述每个部分,并将所有 Runnable 排队到一个 Executor。当所有子部分都完成时,协调线程将能够通过 await。(当线程必须重复这样计数时,而不是使用 CyclicBarrier
。)
class Driver2 { // ...
void main() throws InterruptedException {
CountDownLatch doneSignal = new CountDownLatch(N);
Executor e = ...;
for (int i = 0; i < N; ++i) // 创建并启动线程
e.execute(new WorkerRunnable(doneSignal, i));
doneSignal.await(); // 等待所有线程完成
}
}
class WorkerRunnable implements Runnable {
private final CountDownLatch doneSignal;
private final int i;
WorkerRunnable(CountDownLatch doneSignal, int i) {
this.doneSignal = doneSignal;
this.i = i;
}
public void run() {
doWork();
doneSignal.countDown();
}
void doWork() { ... }
}
内存一致性效果:直到计数达到零,一个线程在调用 countDown()
之前的操作 先行发生 于另一个线程中对应的 await()
成功返回之后的操作。
- 自 JDK 版本:
- 1.5
-
Constructor Summary
-
Method Summary
-
Constructor Details
-
CountDownLatch
public CountDownLatch(int count) 构造一个使用给定计数初始化的CountDownLatch
。- 参数:
-
count
- 在线程可以通过await()
之前必须调用countDown()
的次数 - 抛出:
-
IllegalArgumentException
- 如果count
为负数
-
-
Method Details
-
await
使当前线程等待,直到闩计数减为零,除非线程被中断。如果当前计数为零,则此方法立即返回。
如果当前计数大于零,则当前线程会因为线程调度目的而被禁用,并处于休眠状态,直到发生以下两种情况之一:
- 计数因为调用
countDown()
方法而达到零;或 - 其他线程中断了当前线程。
如果当前线程:
- 在进入此方法时设置了中断状态;或
- 在等待时被中断,
InterruptedException
,并清除当前线程的中断状态。- 抛出:
-
InterruptedException
- 如果当前线程在等待时被中断
- 计数因为调用
-
await
使当前线程等待,直到闩计数减为零,除非线程被中断,或者经过了指定的等待时间。如果当前计数为零,则此方法将立即返回值为
true
。如果当前计数大于零,则当前线程会因为线程调度目的而被禁用,并处于休眠状态,直到发生以下三种情况之一:
- 计数因为调用
countDown()
方法而达到零;或 - 其他线程中断了当前线程;或
- 经过了指定的等待时间。
如果计数达到零,则该方法将返回值为
true
。如果当前线程:
- 在进入此方法时设置了中断状态;或
- 在等待时被中断,
InterruptedException
,并清除当前线程的中断状态。如果指定的等待时间经过,则返回值为
false
。如果时间小于或等于零,则该方法将根本不等待。- 参数:
-
timeout
- 最长等待时间 -
unit
-timeout
参数的时间单位 - 返回:
-
如果计数达到零则返回
true
,如果等待时间在计数达到零之前已过则返回false
- 抛出:
-
InterruptedException
- 如果当前线程在等待时被中断
- 计数因为调用
-
countDown
public void countDown()减少闩的计数,如果计数达到零,则释放所有等待的线程。如果当前计数大于零,则将其递减。如果新计数为零,则所有等待的线程都将重新启用以进行线程调度。
如果当前计数等于零,则不会发生任何事情。
-
getCount
public long getCount()返回当前计数。此方法通常用于调试和测试目的。
- 返回:
- 当前计数
-
toString
返回标识此闩及其状态的字符串。状态在括号中包括字符串"Count ="
后跟当前计数。
-