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

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

执行者框架允许你在不用担心线程创建和执行的情况下,并发的执行任务。它还提供了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章,线程执行者中的运行多个任务并处理第一个结果指南
时间: 2024-09-19 09:08:29

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

定制并发类(六)自定义在计划的线程池内运行的任务

声明:本文是< Java 7 Concurrency Cookbook>的第七章, 作者: Javier Fernández González 译者:郑玉婷 自定义在计划的线程池内运行的任务 计划的线程池是 Executor 框架的基本线程池的扩展,允许你定制一个计划来执行一段时间后需要被执行的任务. 它通过 ScheduledThreadPoolExecutor 类来实现,并允许运行以下这两种任务: Delayed 任务:这种任务在一段时间后仅执行一次. Periodic 任务:这种任务在延

线程执行者(五)运行多个任务并处理第一个结果

运行多个任务并处理第一个结果 在并发编程中的一个常见的问题就是,当有多种并发任务解决一个问题时,你只对这些任务的第一个结果感兴趣.比如,你想要排序一个数组.你有多种排序算法. 你可以全部启用它们,并且获取第一个结果(对于给定数组排序最快的算法的结果). 在这个指南中,你将学习如何使用ThreadPoolExecutor类的场景.你将继续实现一个示例,一个用户可以被两种机制验证.如果使用其中一个机制验证通过,用户将被确认验证通过. 准备工作- 这个指南的例子使用Eclipse IDE实现.如果你使

线程执行者(七)执行者延迟运行一个任务

执行者延迟运行一个任务 执行者框架提供ThreadPoolExecutor类,使用池中的线程来执行Callable和Runnable任务,这样可以避免所有线程的创建操作.当你提交一个任务给执行者,会根据执行者的配置尽快执行它.在有些使用情况下,当你对尽快执行任务不感觉兴趣.你可能想要在一段时间之后执行任务或周期性地执行任务.基于这些目的,执行者框架提供 ScheduledThreadPoolExecutor类. 在这个指南中,你将学习如何创建ScheduledThreadPoolExecutor

线程执行者(八)执行者周期性地运行一个任务

执行者周期性地运行一个任务 执行者框架提供ThreadPoolExecutor类,使用池中的线程执行并发任务,从而避免所有线程的创建操作.当你提交任务给执行者,根据它的配置,它尽快地执行任务.当它结束,任务将被执行者删除,如果你想再次运行任务,你必须再次提交任务给执行者. 但是执行者框架通过ScheduledThreadPoolExecutor类可以执行周期性任务.在这个指南中,你将学习如何通过使用这个类的功能来安排一个周期性任务. 准备工作- 这个指南的例子使用Eclipse IDE实现.如果

线程执行者(四)执行者执行返回结果的任务

执行者执行返回结果的任务 Executor framework的一个优点是你可以并发执行返回结果的任务.Java并发API使用以下两种接口来实现: Callable:此接口有一个call()方法.在这个方法中,你必须实现任务的(处理)逻辑.Callable接口是一个参数化的接口.意味着你必须表明call()方法返回的数据类型. Future:此接口有一些方法来保证Callable对象结果的获取和管理它的状态. 在这个指南中,你将学习如何实现返回结果的任务,并在执行者中运行它们. 准备工作- 这个

线程执行者(九)执行者取消一个任务

执行者取消一个任务 当你使用执行者工作时,你不得不管理线程.你只实现Runnable或 Callable任务和把它们提交给执行者.执行者负责创建线程,在线程池中管理它们,当它们不需要时,结束它们.有时候,你想要取消已经提交给执行者 的任务.在这种情况下,你可以使用Future的cancel()方法,它允许你做取消操作.在这个指南中,你将学习如何使用这个方法来取消已经提交给执行者的任务. 准备工作- 这个指南的例子使用Eclipse IDE实现.如果你使用Eclipse或其他IDE,如NetBea

线程执行者(三)创建一个大小固定的线程执行者

创建一个大小固定的线程执行者 当你使用由Executors类的 newCachedThreadPool()方法创建的基本ThreadPoolExecutor,你会有执行者运行在某一时刻的线程数的问题.这个执行者为每个接收到的任务创建一个线程(如果池中没有空闲的线程),所以,如果你提交大量的任务,并且它们有很长的(执行)时间,你会使系统过载和引发应用程序性能不佳的问题. 如果你想要避免这个问题,Executors类提供一个方法来创建大小固定的线程执行者.这个执行者有最大线程数. 如果你提交超过这个

线程执行者(二)创建一个线程执行者

创建一个线程执行者 使用Executor framework的第一步就是创建一个ThreadPoolExecutor类的对象.你可以使用这个类提供的4个构造器或Executors工厂类来 创建ThreadPoolExecutor.一旦有执行者,你就可以提交Runnable或Callable对象给执行者来执行. 在这个指南中,你将会学习如何使用这两种操作来实现一个web服务器的示例,这个web服务器用来处理各种客户端请求. 准备工作 你应该事先阅读第1章中创建和运行线程的指南,了解Java中线程创

线程执行者(十二)执行者控制被拒绝的任务

执行者控制被拒绝的任务 当你想要结束执行者的执行,你使用shutdown()方法来表明它的结束.执行者等待正在运行或等待它的执行的任务的结束,然后结束它们的执行. 如果你在shutdown()方法和执行者结束之间,提交任务给执行者,这个任务将被拒绝,因为执行者不再接收新的任务.ThreadPoolExecutor类提供一种机制,在调用shutdown()后,不接受新的任务. 在这个指南中,你将学习如何通过实现RejectedExecutionHandler,在执行者中管理拒绝任务. 准备工作-