Java异步判断线程池所有任务是否执行完成的方法
1.使用ExecutorService
和CountDownLatch
的方法示例
在Java中,当我们使用线程池(如ExecutorService
)来执行异步任务时,常常需要知道所有任务是否都已经完成。ExecutorService
接口提供了几种方式来处理这种情况,但最常用的是shutdown()
和awaitTermination()
方法的组合,或者使用Future
和CompletionService
。这里我将提供一个使用ExecutorService
和CountDownLatch
的示例,因为CountDownLatch
提供了一种直观的方式来等待一组线程完成。
首先,我们定义几个任务,然后使用ExecutorService
来异步执行它们,并使用CountDownLatch
来等待所有任务完成。
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) throws InterruptedException {
// 创建一个包含固定数量线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
// 定义任务数量
int taskCount = 10;
// 使用CountDownLatch来等待所有任务完成
final CountDownLatch latch = new CountDownLatch(taskCount);
// 提交任务到线程池
for (int i = 0; i < taskCount; i++) {
int taskId = i;
executorService.submit(() -> {
// 模拟任务执行
try {
Thread.sleep(1000); // 假设每个任务需要1秒
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("任务 " + taskId + " 完成");
// 每完成一个任务,计数减一
latch.countDown();
});
}
// 等待所有任务完成
System.out.println("等待所有任务完成...");
latch.await(); // 阻塞当前线程,直到latch的计数达到零
System.out.println("所有任务完成!");
// 关闭线程池
executorService.shutdown();
// 可选:等待线程池中的线程都执行完毕
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
// 线程池没有在规定时间内关闭,则强制关闭
executorService.shutdownNow();
}
} catch (InterruptedException e) {
// 当前线程在等待过程中被中断
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
在这个例子中,我们首先创建了一个固定大小的线程池(这里使用4个线程)。然后,我们定义了一个CountDownLatch
,其计数被初始化为任务的数量(这里为10)。对于每个任务,我们都向线程池提交了一个Runnable
,其中包含了任务的执行逻辑和latch.countDown()
调用,以确保每次任务完成时都会减少CountDownLatch
的计数。
主线程通过调用latch.await()
来等待,直到所有任务都调用了countDown()
(即计数达到零),然后才能继续执行。这确保了主线程会等待所有任务完成后再继续。
最后,我们关闭了线程池,并通过调用awaitTermination()
来可选地等待线程池中的所有线程都执行完毕。如果线程池没有在指定时间内关闭,则调用shutdownNow()
来尝试立即停止所有正在执行的任务。
这个示例提供了处理异步任务并等待它们完成的一种有效方式,适用于需要等待所有任务完成再继续的场景。
2.使用ExecutorService
的invokeAll
方法和Future
列表的方法示例
除了使用CountDownLatch
之外,还有其他方法可以判断线程池中的所有任务是否执行完成。以下是一个使用ExecutorService
的invokeAll
方法和Future
列表的示例,这种方法适用于我们有一组已知的任务(Callable
)需要并行执行,并且我们需要等待所有任务完成并获取它们的结果。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class ThreadPoolFutureExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
// 创建一个包含固定数量线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
// 创建一个Callable任务列表
List<Callable<String>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int taskId = i;
tasks.add(() -> {
// 模拟任务执行
Thread.sleep(1000); // 假设每个任务需要1秒
return "任务 " + taskId + " 完成";
});
}
// 使用invokeAll提交所有任务,这将返回一个Future列表
List<Future<String>> futures = executorService.invokeAll(tasks);
// 遍历Future列表,获取每个任务的结果
for (Future<String> future : futures) {
// get()会阻塞,直到对应的任务完成
System.out.println(future.get());
}
// 关闭线程池
executorService.shutdown();
// 可选:等待线程池中的线程都执行完毕
try {
if (!executorService.awaitTermination(60, TimeUnit.SECONDS)) {
// 线程池没有在规定时间内关闭,则强制关闭
executorService.shutdownNow();
}
} catch (InterruptedException e) {
// 当前线程在等待过程中被中断
executorService.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
// 注意:这里使用了Lambda表达式和方法引用来简化Callable的创建
// 实际使用中,你可能需要实现Callable接口或使用匿名内部类
在这个例子中,我们创建了一个ExecutorService
和一个Callable
任务列表。每个Callable
任务都会返回一个字符串,表示任务完成的信息。我们使用invokeAll
方法提交了所有任务,并立即获得了一个Future
列表,每个Future
都代表了一个任务的执行结果。
然后,我们遍历这个Future
列表,并对每个Future
调用get()
方法。get()
方法会阻塞当前线程,直到对应的任务完成并返回结果。这样,我们就能确保在继续执行之前,所有任务都已经完成。
最后,我们关闭了线程池,并等待所有线程都执行完毕(或超时后强制关闭)。
请注意,虽然这个示例使用了Callable
和Future
,但它并没有直接提供一个“是否所有任务都已完成”的布尔值。然而,通过遍历Future
列表并调用get()
,我们实际上已经达到了等待所有任务完成的效果。如果我们只需要知道是否所有任务都已开始执行(而不是等待它们完成),那么我们可能需要采用不同的策略,比如使用execute
方法结合其他同步机制(如CountDownLatch
)。
3.使用ExecutorService
来异步执行多个Callable
任务方法示例
以下是一个详细完整的代码示例,该示例使用了ExecutorService
来异步执行多个Callable
任务,并通过遍历Future
列表来等待所有任务完成并获取它们的结果。
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class ThreadPoolFutureCompleteExample {
public static void main(String[] args) {
// 创建一个包含固定数量线程的线程池
ExecutorService executorService = Executors.newFixedThreadPool(4);
// 创建一个Callable任务列表
List<Callable<String>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int taskId = i;
tasks.add(new Callable<String>() {
@Override
public String call() throws Exception {
// 模拟任务执行
TimeUnit.SECONDS.sleep(1); // 假设每个任务需要1秒
return "任务 " + taskId + " 完成";
}
});
// 或者使用Lambda表达式(如果你使用的是Java 8或更高版本)
// tasks.add(() -> {
// TimeUnit.SECONDS.sleep(1);
// return "任务 " + taskId + " 完成";
// });
}
try {
// 使用invokeAll提交所有任务,这将返回一个Future列表
List<Future<String>> futures = executorService.invokeAll(tasks);
// 遍历Future列表,获取每个任务的结果
for (Future<String> future : futures) {
// get()会阻塞,直到对应的任务完成
System.out.println(future.get());
}
// 关闭线程池
executorService.shutdown();
// 等待线程池中的所有线程都执行完毕(可选)
// 注意:由于我们已经调用了invokeAll并等待了所有Future的完成,这一步通常是多余的
// 但为了完整性,我还是展示了如何等待线程池关闭
boolean terminated = executorService.awaitTermination(60, TimeUnit.SECONDS);
if (!terminated) {
// 如果线程池没有在规定时间内关闭,则强制关闭
System.err.println("线程池没有在规定时间内关闭,尝试强制关闭...");
executorService.shutdownNow();
// 注意:shutdownNow()不保证已经提交的任务会被取消
// 它会尝试停止正在执行的任务,但已经开始执行的任务可能无法被中断
}
} catch (InterruptedException | ExecutionException e) {
// 处理异常
e.printStackTrace();
// 如果当前线程在等待过程中被中断,尝试关闭线程池
if (!executorService.isShutdown()) {
executorService.shutdownNow();
}
// 根据需要,可能还需要重新设置中断状态
Thread.currentThread().interrupt();
}
}
}
在这个示例中,我使用了传统的匿名内部类来创建Callable
任务(同时也提供了Lambda表达式的注释),以便与各种Java版本兼容。然而,如果我们正在使用Java 8或更高版本,我强烈推荐我们使用Lambda表达式来简化代码。
请注意,invokeAll
方法会阻塞调用它的线程,直到所有任务都完成,或者直到等待超时(如果我们提供了超时时间)。但是,在这个示例中,我们没有为invokeAll
提供超时时间,因此它会一直等待,直到所有任务都完成。
另外,请注意,在catch
块中,如果捕获到InterruptedException
,我们检查了线程池是否已经被关闭(使用isShutdown
方法)。如果没有,我们调用shutdownNow
方法来尝试关闭线程池并停止正在执行的任务。然而,需要注意的是,shutdownNow
方法并不保证能够停止所有已经开始执行的任务,因为某些任务可能无法被中断。
最后,如果在捕获到InterruptedException
后,我们确定当前线程需要被重新中断(比如,我们在一个循环中等待某个条件,而中断是用来退出循环的),那么我们应该调用Thread.currentThread().interrupt()
来重新设置中断状态。在这个示例中,我们没有这样做,因为main
方法不需要重新中断。但是,在更复杂的场景中,这可能是必要的。
Java异步判断线程池所有任务是否执行完成的方法的更多相关文章
- Java如何判断线程池所有任务是否执行完毕
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Tes ...
- Java异步、线程池解决方案
一.ThreadPoolExecutor------线程池 private static final ThreadPoolExecutor threadPoolExecutor = new Threa ...
- java并发包&线程池原理分析&锁的深度化
java并发包&线程池原理分析&锁的深度化 并发包 同步容器类 Vector与ArrayList区别 1.ArrayList是最常用的List实现类,内部是通过数组实现的, ...
- JAVA多线程(三) 线程池和锁的深度化
github演示代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-servic ...
- Java 并发编程 | 线程池详解
原文: https://chenmingyu.top/concurrent-threadpool/ 线程池 线程池用来处理异步任务或者并发执行的任务 优点: 重复利用已创建的线程,减少创建和销毁线程造 ...
- Java并发包线程池之Executors、ExecutorCompletionService工具类
前言 前面介绍了Java并发包提供的三种线程池,它们用处各不相同,接下来介绍一些工具类,对这三种线程池的使用. Executors Executors是JDK1.5就开始存在是一个线程池工具类,它定义 ...
- Java并发包线程池之ScheduledThreadPoolExecutor
前言 它是一种可以安排在给定的延迟之后执行一次或周期性执行任务的ThreadPoolExecutor.因为它继承了ThreadPoolExecutor, 当然也具有处理普通Runnable.Calla ...
- Java多线程与线程池技术
一.序言 Java多线程编程线程池被广泛使用,甚至成为了标配. 线程池本质是池化技术的应用,和连接池类似,创建连接与关闭连接属于耗时操作,创建线程与销毁线程也属于重操作,为了提高效率,先提前创建好一批 ...
- Java 四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor
介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行一个异步任务你还只是如下new T ...
- Java四种线程池
Java四种线程池newCachedThreadPool,newFixedThreadPool,newScheduledThreadPool,newSingleThreadExecutor 时间:20 ...
随机推荐
- windows 文件夹添加备注
1,选中希望改动的文件夹,然后右键"单击",选择"属性"按钮. 2,打开"自定义"面板,选择"更改图标",将原来的默认文 ...
- Redis 的安装与配置详解【Redis系列一】
〇.前言 关于 Redis 在日常开发中还是用的比较多的,特别是在秒杀.消息队列.排行榜等数据交互时效要求较高的场景,Redis 都可以轻松应对. 本文将针对 Redis 进行简单介绍,以及如何安装, ...
- linux下date命令设置时间的输出格式和修改时间
目录 一.关于linux下的时间 二.linux下使用date命令设置时间的输出格式 三.修改linux时间 3.1 在可以访问Internet情况下修改时间 3.2 在无法访问Internet情况下 ...
- 基于 ESP8266_RTOS_SDK 驱动 DHT11
概述 DHT11模块使用一根data线实现信号触发以及数据反馈,信号格式参考如下 https://zhuanlan.zhihu.com/p/347904660 本文使用GPIO中断的方式采集反馈数据 ...
- flutter开发环境的搭建
下载flutter开发包,有670M左右. github的下载地址:https://github.com/flutter/flutter 或者官方下载地址:https://flutter.dev/do ...
- jQuery模态框原理
<!-- 引入jQuery.js --> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquer ...
- HTML——文件上传域
文件上传是网站中一种常见的功能.例如百度网盘.QQ 邮箱以及有道云笔记都可以实现文件的上传.在 HTML 中,把 <input> 标签的 type 属性设置为 file 就可以实现上传文件 ...
- go语言开发的内网穿透工具,frp.
转载自:https://www.appinn.com/frp/ 什么是 Frp? 内网穿透工具有很多,其中 Frp (Fast Reverse Proxy) 是比较流行的一款.FRP 是一个免费开源的 ...
- golang开发 gorilla websocket的使用
很多APP都需要主动向用户推送消息,这就需要用到长连接的服务,即我们通常提到的websocket,同样也是使用socket服务,通信协议是基本类似的,在go中用的最多的.也是最简单的socket服务就 ...
- FRDM-MCXN947开发板之RGB灯
一.背景 RGB LED:通过红.绿.蓝三种颜色组合发光的LED,可以理解由三个不同发光属性的LED组成,这个是LCD平板显示原理的基础,一个LED相当于屏幕上面的一个像素 FRDM-MCXN947集 ...