CountDownLatch是一个通用同步器,用于同步一个或多个任务。在完成一组正在其他线程中执行的任务之前,它允许一个或多个线程一直等待。
可以用一个初始计数值来初始化CountDownLatch对象,任何在这个对象上调用wait()的方法都将阻塞,直至计数值到达0。每完成一个任务,都可以在这个对象上调用countDown()减少计数值。当计数值减为0,所有等待的线程都会被释放。CountDownLatch的计数值不能重置。如果需要重置计数器,请考虑使用CyclicBarrier。
使用场景
作为一个通用同步工具,CountDownLatch有许多用途。比如,将计数值初始化为1的CountDownLatch用作一个简单的开/关:在通过调用countDown()的线程打开入口前,所有调用await()的线程都一直在入口处等待;用N初始化的 CountDownLatch可以使一个线程在N个线程完成某项操作之前一直等待,或者使其在某项操作完成N次之前一直等待。
例1:CountDownLatch作为开关
public class Driver {
public static void main(String args[]) throws InterruptedException {
int n = 5;
// 启动信号,在driver为继续执行worker做好准备之前,它会阻止所有的worker继续执行。
CountDownLatch startSignal = new CountDownLatch(1);
// 完成信号,它允许driver在完成所有 worker之前一直等待。
CountDownLatch doneSignal = new CountDownLatch(n);
for (int i = 0; i < n; ++i) // 创建并启动所有线程
new Thread(new Worker(startSignal, doneSignal)).start();
Thread.sleep(1000);
startSignal.countDown(); // 打开startSignal开关,执行所有等待的任务
doneSignal.await();// 等待doneSignal计数器为0,即所有任务执行完
System.out.println("down");
}
}
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 {
System.out.println(Thread.currentThread().getName() + "启动了");
startSignal.await();// 使当前线程在锁存器startSignal倒计数至零之前一直等待
System.out.println(Thread.currentThread().getName() + "工作了");
doneSignal.countDown();// 递减锁存器doneSignal的计数
} catch (InterruptedException ex) {
}
}
}
执行结果为:
Thread-2启动了
Thread-0启动了
Thread-1启动了
Thread-3启动了
Thread-4启动了
Thread-2工作了
Thread-3工作了
Thread-1工作了
Thread-4工作了
Thread-0工作了
down
从结果中看到,5个任务线程依次启动,但每一个都没有执行完,这是因为run方法中的startSignal.await();
使当前线程在锁存器startSignal倒计数至零之前一直等待。直到startSignal.countDown();
打开startSignal开关,才执行所有等待的任务。
例2:CountDownLatch分割任务
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Driver2 {
public static void main(String args[]) throws InterruptedException {
int n = 5;
ExecutorService exec = Executors.newCachedThreadPool();
// 完成信号,它允许driver在完成所有 worker之前一直等待。
CountDownLatch doneSignal = new CountDownLatch(n);
// 创建并启动所有线程
for (int i = 0; i < n; ++i)
exec.execute(new WorkerRunnable(doneSignal, i));
// 等待doneSignal计数器为0,即所有任务执行完
doneSignal.await();
System.out.println("down");
exec.shutdown();
}
}
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() {
System.out.println("任务" + i + "完成了");
doneSignal.countDown();// 递减锁存器doneSignal的计数
}
}
源码
待补充
本文就讲到这里,想了解Java并发编程更多内容请参考:
- Java并发编程札记-目录