一,CountDownLatch
CountDownLatch
是Java并发编程中用于线程间协调的一个同步辅助类。它通过一个初始计数值来控制线程的等待,这个计数值在其他线程执行特定任务时递减。
-
初始化 :创建
CountDownLatch
实例时,你需要指定一个整数值,该值代表需要等待的事件或任务的数量。 -
递减计数 :
countDown()
方法用于减少CountDownLatch
的当前计数值。每当一个关联的操作执行完毕,就调用此方法,从而向等待的线程发出信号。 -
等待完成 :
await()
方法用于阻塞当前线程,直到CountDownLatch
的计数值达到0。这意味着主线程或其他等待的线程会在执行了await()
调用的地方暂停,直到所有相关的countDown()
操作完成。 -
灵活性 :
countDown()
可以被同一个线程多次调用,每次调用都会导致计数值减少。同时,如果有多个线程都在等待,它们会共享同一个CountDownLatch
的计数值,当计数值减到0时,所有等待的线程都会被唤醒。 -
超时机制 :
await()
方法还可以接受一个超时时间参数,这样即使计数值没有在规定时间内减到0,等待的线程也会被唤醒,避免无限期等待。 -
结果汇总:在所有任务执行完毕后,主线程可以安全地继续执行,此时可以对子线程中的任务结果进行汇总或后续处理。
通过这种方式,CountDownLatch
确保了在所有预定的任务或事件完成之前,主线程或其他线程不会过早地继续执行,从而实现了线程间的协调和同步。
二,示例
java
public class 多线程结合countdownlatch {
public static void main(String[] args) {
//模拟任务
List<String> records = new ArrayList<>();
records.add("rc1");
records.add("rc2");
//线程池
System.out.println("开始执行");
ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
CountDownLatch latch = new CountDownLatch(records.size());
records.forEach(record->{
service.execute(new Runnable() {
@Override
public void run() {
try {
System.out.println(record);
Thread.sleep(2000);//执行业务逻辑
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
latch.countDown();
}
}
});
});
try {
latch.await(30, TimeUnit.MINUTES);//设置超时时间 防止无限期等待
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("执行完成");
}
}
三,使用场景
场景一:CountDownLatch 非常适合于对任务进行拆分,使其并行执行,比如某个任务执行2s,其对数据的请求可以分为五个部分,那么就可以将这个任务拆分为5个子任务,分别交由五个线程执行,执行完成之后再由主线程进行汇总,此时,总的执行时间将决定于执行最慢的任务,平均来看,还是大大减少了总的执行时间。
场景二:使用 CountDownLatch 的地方是使用某些外部链接请求数据的时候,比如图片。在本人所从事的项目中就有类似的情况,因为我们使用的图片服务只提供了获取单个图片的功能,而每次获取图片的时间不等,一般都需要1.5s~2s。当我们需要批量获取图片的时候,比如列表页需要展示一系列的图片,如果使用单个线程顺序获取,那么等待时间将会极长,此时我们就可以使用CountDownLatch对获取图片的操作进行拆分,并行的获取图片,这样也就缩短了总的获取时间。