10 CountDownLatch
本文为个人学习摘要笔记。 原文地址:CountDownLatch 详解
CountDownLatch 中 count down 是倒数的意思,latch 则是门闩的含义。整体含义可以理解为倒数的门栓,似乎有一点“三二一,芝麻开门”的感觉。
在构造 CountDownLatch 的时候需要传入一个整数 n,在这个整数“倒数”到 0 之前,主线程需要等待在门口,而这个“倒数”过程则是由各个执行线程驱动的,每个线程执行完一个任务“倒数”一次。
总结来说,CountDownLatch 的作用就是等待其他的线程都执行完任务,必要时可以对各个任务的执行结果进行汇总,然后主线程才继续往下执行。
CountDownLatch 类只提供了一个构造器和三个主要方法:
public CountDownLatch(int count) {}; // 参数 count 为计数值
public void await() throws InterruptedException {}; // 调用 await() 方法的线程会被挂起,它会等待直到 count 值为 0 才继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException {}; // 和 await() 类似,只不过等待一定的时间后 count 值还没变为 0 的话就会继续执行
public void countDown() {}; // 将 count 值减 1countDown() 方法用于使计数器减一,一般是执行任务的线程调用,await() 方法则使调用该方法的线程处于等待状态,一般是主线程调用。直到某个线程将计数器倒数至 0,其就会唤醒在 await() 方法中等待的线程。
这里需要注意的是,countDown() 方法并没有规定一个线程只能调用一次,当同一个线程调用多次 countDown() 方法时,每次都会使计数器减一;另外,await() 方法也并没有规定只能有一个线程执行该方法,如果多个线程同时执行 await() 方法,那么这几个线程都将处于等待状态,并且以共享模式享有同一个锁。
举个栗子:
public class T {
public static void main(String[] args) throws InterruptedException {
final int size = 5;
CountDownLatch countDownLatch = new CountDownLatch(size);
for (int i = 0; i < size; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " start");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " end");
countDownLatch.countDown();
}, "Thread" + i).start();
}
System.out.println("main thread await");
countDownLatch.await();
System.out.println("main thread finishes await");
}
}将上面栗子改造下,使用 try...finally 结构,保证创建的线程发生异常时 CountDownLatch.countDown() 方法也会执行,也就保证了主线程不会一直处于等待状态。
CountDownLatch 非常适合于对任务进行拆分,使其并行执行,比如某个任务执行 2s,其对数据的请求可以分为五个部分,那么就可以将这个任务拆分为 5 个子任务,分别交由五个线程执行,执行完成之后再由主线程进行汇总,此时,总的执行时间将决定于执行最慢的任务,平均来看,还是大大减少了总的执行时间。
另外一种比较合适使用 CountDownLatch 的地方是使用某些外部链接请求数据的时候,比如批量获取图片,如果使用单个线程顺序获取,那么等待时间将会极长,此时就可以使用 CountDownLatch 对获取图片的操作进行拆分,并行的获取图片,这样也就缩短了总的获取时间。
最后更新于
这有帮助吗?