线程执行者(六)运行多个任务并处理所有结果

简介:

运行多个任务并处理所有结果

执行者框架允许你在不用担心线程创建和执行的情况下,并发的执行任务。它还提供了Future类,这个类可以用来控制任务的状态,也可以用来获得执行者执行任务的结果。

如果你想要等待一个任务完成,你可以使用以下两种方法:

  • 如果任务执行完成,Future接口的isDone()方法将返回true。
  • ThreadPoolExecutor类的awaitTermination()方法使线程进入睡眠,直到每一个任务调用shutdown()方法之后完成执行。

这两种方法都有一些缺点。第一个方法,你只能控制一个任务的完成。第二个方法,你必须等待一个线程来关闭执行者,否则这个方法的调用立即返回。

ThreadPoolExecutor类提供一个方法,允许你提交任务列表给执行者,并且在这个列表上等待所有任务的完成。在这个指南中,你将学习如何使用这个特性,实现一个示例,执行3个任务,并且当它们完成时将结果打印出来。

准备工作…

这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。

如何做…

按以下步骤来实现的这个例子:

1.创建Result类,存储这个示例中并发任务产生的结果。


1 public class Result {

2.声明两个私有属性。一个String属性,名为name,另一个int属性,名为value。


1 private String name;
2 private int value;

3.实现相应的get()和set()方法,用来设置和获取name和value属性的值。


01 public String getName() {
02 return name;
03 }
04 public void setName(String name) {
05 this.name = name;
06 }
07 public int getValue() {
08  
09 return value;
10 }
11 public void setValue(int value) {
12 this.value = value;
13 }

4.创建Task类,实现Callable接口,参数化为Result类型。


1 public class Task implements Callable<Result> {

5.声明一个私有String属性,名为name。


1 private String name;

6.实现Task类构造器,初始化这个属性。


1 public Task(String name) {
2 this.name=name;
3 }

7.实现这个类的call()方法,在本例中,它将返回一个Result对象。


1 @Override
2 public Result call() throws Exception {

8.首先,写入一个信息到控制台,表明任务开始。


1 System.out.printf("%s: Staring\n",this.name);

9.然后,等待一个随机时间。


1 try {
2 long duration=(long)(Math.random()*10);
3 System.out.printf("%s: Waiting %d seconds for results.\n",this.name,duration);
4 TimeUnit.SECONDS.sleep(duration);
5 } catch (InterruptedException e) {
6 e.printStackTrace();
7 }

10.在Result对象中返回一个计算5个随机数的总和的int值。


1 int value=0;
2 for (int i=0; i<5; i++){
3 value+=(int)(Math.random()*100);
4 }

11.创建Result对象,用任务的名称和前面操作结果来初始化它。


1 Result result=new Result();
2 result.setName(this.name);
3 result.setValue(value);

12.写入一个信息到控制台,表明任务已经完成。


1 System.out.println(this.name+": Ends");

13.返回Result对象。


1 return result;
2 }

14.最后,实现这个示例的主类,创建Main类,实现main()方法。


1 public class Main {
2 public static void main(String[] args) {

15.使用Executors类的newCachedThreadPool()方法,创建ThreadPoolExecutor对象。


1 ExecutorService executor=(ExecutorService)Executors.newCachedThreadPool();

16.创建Task对象列表。创建3个Task对象并且用这个列表来存储。


1 List<Task> taskList=new ArrayList<>();
2 for (int i=0; i<3; i++){
3 Task task=new Task(i);
4 taskList.add(task);
5 }

17.创建Future对象列表,参数化为Result类型。


1 List<Future<Result>>resultList=null;

18.调用ThreadPoolExecutor类的invokeAll()方法。这个类将会返回之前创建的Future对象列表。


1 try {
2 resultList=executor.invokeAll(taskList);
3 } catch (InterruptedException e) {
4 e.printStackTrace();
5 }

19.使用shutdown()方法结束执行者。


1 executor.shutdown();

20.写入处理Future对象列表任务的结果。


01 System.out.println("Main: Printing the results");
02 for (int i=0; i<resultList.size(); i++){
03 Future<Result> future=resultList.get(i);
04 try {
05 Result result=future.get();
06 System.out.println(result.getName()+": "+result.
07 getValue());
08 } catch (InterruptedException | ExecutionException e) {
09 e.printStackTrace();
10 }
11 }

它是如何工作的…

在这个指南中,你已经学习了如何提交任务列表到执行者和使用invokeAll()方法等待它们的完成。这个方法接收Callable对象列表和返回 Future对象列表。这个列表将会有列表中每个任务的一个Future对象。Future对象列表的第一个对象是Callable对象列表控制的第一个任务,以此类推。

第一点要考虑的是,在存储结果对象的列表中声明的Future接口参数化的数据类型必须与使用的Callable对象的参数化相兼容。在本例中,你已经使用相同数据类型:Result类型。

另一个重要的一点就是关于invokeAll()方法,你会使用Future对象获取任务的结果。当所有的任务完成时,这个方法结束,如果你调用返回的Future对象的isDone()方法,所有调用都将返回true值。

不止这些…

ExecutorService类提供其他版本的invokeAll()方法:

  • invokeAll(Collection<? extends Callable<T>> tasks, long timeout,TimeUnit unit):此方法执行所有任务,当它们全部完成且未超时,返回它们的执行结果。TimeUnit类是个枚举类,有如下常量:DAYS,HOURS,MICROSECONDS, MILLISECONDS, MINUTES,,NANOSECONDS 和SECONDS。

参见

  • 在第4章,线程执行者中的执行者执行返回结果的任务指南
  • 在第4章,线程执行者中的运行多个任务并处理第一个结果指南
目录
相关文章
|
3月前
|
存储 Java 数据库
如何处理线程池关闭时未完成的任务?
总之,处理线程池关闭时未完成的任务需要综合考虑多种因素,并根据实际情况选择合适的处理方式。通过合理的处理,可以最大程度地减少任务丢失和数据不一致等问题,确保系统的稳定运行和业务的顺利开展。
173 64
|
1月前
|
监控 Java
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
109 17
|
3月前
|
消息中间件 监控 Java
线程池关闭时未完成的任务如何保证数据的一致性?
保证线程池关闭时未完成任务的数据一致性需要综合运用多种方法和机制。通过备份与恢复、事务管理、任务状态记录与恢复、数据同步与协调、错误处理与补偿、监控与预警等手段的结合,以及结合具体业务场景进行分析和制定策略,能够最大程度地确保数据的一致性,保障系统的稳定运行和业务的顺利开展。同时,不断地优化和改进这些方法和机制,也是提高系统性能和可靠性的重要途径。
143 62
|
3月前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
76 12
|
4月前
|
缓存 负载均衡 Java
c++写高性能的任务流线程池(万字详解!)
本文介绍了一种高性能的任务流线程池设计,涵盖多种优化机制。首先介绍了Work Steal机制,通过任务偷窃提高资源利用率。接着讨论了优先级任务,使不同优先级的任务得到合理调度。然后提出了缓存机制,通过环形缓存队列提升程序负载能力。Local Thread机制则通过预先创建线程减少创建和销毁线程的开销。Lock Free机制进一步减少了锁的竞争。容量动态调整机制根据任务负载动态调整线程数量。批量处理机制提高了任务处理效率。此外,还介绍了负载均衡、避免等待、预测优化、减少复制等策略。最后,任务组的设计便于管理和复用多任务。整体设计旨在提升线程池的性能和稳定性。
120 5
|
5月前
|
Java Spring
运行@Async注解的方法的线程池
自定义@Async注解线程池
210 3
|
6月前
|
前端开发 JavaScript 大数据
React与Web Workers:开启前端多线程时代的钥匙——深入探索计算密集型任务的优化策略与最佳实践
【8月更文挑战第31天】随着Web应用复杂性的提升,单线程JavaScript已难以胜任高计算量任务。Web Workers通过多线程编程解决了这一问题,使耗时任务独立运行而不阻塞主线程。结合React的组件化与虚拟DOM优势,可将大数据处理等任务交由Web Workers完成,确保UI流畅。最佳实践包括定义清晰接口、加强错误处理及合理评估任务特性。这一结合不仅提升了用户体验,更为前端开发带来多线程时代的全新可能。
156 1
|
6月前
|
存储 监控 Java
|
6月前
|
消息中间件 设计模式 安全
多线程魔法:揭秘一个JVM中如何同时运行多个消费者
【8月更文挑战第22天】在Java虚拟机(JVM)中探索多消费者模式,此模式解耦生产与消费过程,提升系统性能。通过`ExecutorService`和`BlockingQueue`构建含2个生产者及4个消费者的系统,实现实时消息处理。多消费者模式虽增强处理能力,但也引入线程安全与资源竞争等挑战,需谨慎设计以确保高效稳定运行。
112 2
|
4天前
|
Python
python3多线程中使用线程睡眠
本文详细介绍了Python3多线程编程中使用线程睡眠的基本方法和应用场景。通过 `time.sleep()`函数,可以使线程暂停执行一段指定的时间,从而控制线程的执行节奏。通过实际示例演示了如何在多线程中使用线程睡眠来实现计数器和下载器功能。希望本文能帮助您更好地理解和应用Python多线程编程,提高程序的并发能力和执行效率。
33 20

热门文章

最新文章

相关实验场景

更多