CountDownLatch
是一个同步工具类,它使用给定的 count
初始化, await()
方法会一直阻塞,直到计数器的值变为零(由于 countDown()
方法被调用导致的),这时会释放所有等待的线程,且之后再调用 await()
方法会直接返回,不会阻塞。 CountDownLatch
是一个 一次性的类,计数器不能被重置,这一点与 CyclicBarrier
不同。另一个不同点是: CountDownLatch
是让所有线程 等待计数器的值变为零再继续执行;而 CyclicBarrier
是要 等待指定个数的线程到达 Barrier 的位置再一起继续执行。
计数器的初始值为count
,也就是说countDown()
方法至少被调用count
次等待的线程才会被唤醒。如果count
为负数或抛出异常IllegalArgumentException
。
如果当前计数器的值大于零,则将其减一,如果新的计数器值等于零,则释放所有等待的线程。
如果当前计数器为零,则什么都不做。
此方法不会阻塞。
获取当前计数器的值。
public static void test() throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(3);
System.out.println(cdl.getCount());
cdl.countDown();
System.out.println(cdl.getCount());
cdl.countDown();
System.out.println(cdl.getCount());
cdl.countDown();
System.out.println(cdl.getCount());
cdl.countDown();
System.out.println(cdl.getCount());
cdl.countDown();
System.out.println(cdl.getCount());
}
计数器变为零后就不会再减了。
3
2
1
0
0
0
导致当前线程等待,直到计数器的值变为零,除非线程被中断。如果计数器已经为零了,则立即返回。以下两种情况会抛出InterruptedException
并清空中断标志:
wait()
方法前,当前线程的中断状态已经为 true 了模拟两种中断情况
// 在调用`wait()`方法前,当前线程的中断状态已经为 true 了
public static void test1() throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(1);
Thread.currentThread().interrupt();
cdl.await();
}
// 在等待的过程中被中断了
public static void test2() throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(1);
Thread t1 = new Thread(() -> {
try {
cdl.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1");
t1.start();
Thread.sleep(500);
t1.interrupt();
}
此方法与await()
的不同点:
等待指定时间
public static void test3() throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(1);
log.info("开始 await");
boolean b = cdl.await(2, TimeUnit.SECONDS);
log.info("结束 await, 返回值: {}", b);
}
等待 2 秒后返回了,且返回值为 false
16:33:11.492 [main] INFO com.example.heima.concurrent.CountDownLatchTest - 开始 await
16:33:13.503 [main] INFO com.example.heima.concurrent.CountDownLatchTest - 结束 await, 返回值: false
计数器在等待过程中变为零
public static void test4() throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(1);
Thread t1 = new Thread(() -> {
try {
log.info("开始 await");
// 至多等待 2 秒
boolean b = cdl.await(2, TimeUnit.SECONDS);
log.info("结束 await, 返回值: {}", b);
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "t1");
t1.start();
Thread.sleep(1000);
cdl.countDown();
}
等待 1 秒后返回了,且返回值为 true
16:36:20.408 [t1] INFO com.example.heima.concurrent.CountDownLatchTest - 开始 await
16:36:21.421 [t1] INFO com.example.heima.concurrent.CountDownLatchTest - 结束 await, 返回值: true
计数器在调用await()方法前就变为 0 了
public static void test5() throws InterruptedException {
CountDownLatch cdl = new CountDownLatch(1);
cdl.countDown();
log.info("开始 await");
boolean b = cdl.await(2, TimeUnit.SECONDS);
log.info("结束 await, 返回值: {}", b);
}
不会等待,且返回值为 true
16:38:33.047 [main] INFO com.example.heima.concurrent.CountDownLatchTest - 开始 await
16:38:33.054 [main] INFO com.example.heima.concurrent.CountDownLatchTest - 结束 await, 返回值: true
以下代码均来源于源码的注释
Driver、Worker
下面是两个类,其中一组 Worker 线程使用了两个CountDownLatch
:
class Driver { // ...
public static void main(String[] args) throws InterruptedException {
int N = 5;
CountDownLatch startSignal = new CountDownLatch(1);
CountDownLatch doneSignal = new CountDownLatch(N);
for (int i = 0; i < N; ++i) // create and start threads
new Thread(new Worker(startSignal, doneSignal)).start();
System.out.println("doSomethingElse"); // don't let run yet
startSignal.countDown(); // let all threads proceed
System.out.println("doSomethingElse");
doneSignal.await(); // wait for all to finish
System.out.println("all worker completed");
}
}
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) {} // return;
}
void doWork() {
System.out.println("doWork");
}
}
将一个问题分解为多个部分
class Driver2 { // ...
public static void main(String[] args) throws InterruptedException {
int N = 3;
CountDownLatch doneSignal = new CountDownLatch(N);
Executor e = Executors.newFixedThreadPool(N);
// i 代表是问题的第几部分
for (int i = 0; i < N; ++i) // create and start threads
e.execute(new WorkerRunnable(doneSignal, i));
doneSignal.await(); // wait for all to finish
System.out.println("all task completed");
}
}
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(i);
doneSignal.countDown();
}
void doWork(int i) {
System.out.println("task " + i);
}
}