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

简介:

运行多个任务并处理第一个结果

在并发编程中的一个常见的问题就是,当有多种并发任务解决一个问题时,你只对这些任务的第一个结果感兴趣。比如,你想要排序一个数组。你有多种排序算法。 你可以全部启用它们,并且获取第一个结果(对于给定数组排序最快的算法的结果)。

在这个指南中,你将学习如何使用ThreadPoolExecutor类的场景。你将继续实现一个示例,一个用户可以被两种机制验证。如果使用其中一个机制验证通过,用户将被确认验证通过。

准备工作…

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

如何做…

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

1.创建UserValidator类,实现用户验证过程。


1 public class UserValidator {

2.声明一个私有的、类型为String、名为name的属性,用来存储系统验证用户的名称。


1 private String name;

3.实现UserValidator类的构造器,初始化这个属性。


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

4.实现validate()方法。接收你想要验证用户的两个String类型参数,一个为name,一个为password。


1 public boolean validate(String name, String password) {

5.创建Random对象,名为random。


1 Random random=new Random();

6.等待个随机时间,用来模拟用户验证的过程。


1 try {
2 long duration=(long)(Math.random()*10);
3 System.out.printf("Validator %s: Validating a user during %d seconds\n",this.name,duration);
4 TimeUnit.SECONDS.sleep(duration);
5 } catch (InterruptedException e) {
6 return false;
7 }

7.返回一个随机Boolean值。如果用户验证通过,这个方法将返回true,否则,返回false。


1 return random.nextBoolean();
2 }

8.实现getName()方法,返回name属性值。


1 public String getName(){
2 return name;
3 }

9.现在,创建TaskValidator类,用来执行UserValidation对象作为并发任务的验证过程。指定它实现Callable接口,并参数化为String类型。


1 public class TaskValidator implements Callable<String> {

10.声明一个私有的、类型为UserValidator、名为validator的属性。


1 private UserValidator validator;

11.声明两个私有的、类型为String、名分别为user和password的属性。


1 private String user;
2 private String password;

12.实现TaskValidator类,初始化这些属性。


1 public TaskValidator(UserValidator validator, String user,
2 String password){
3 this.validator=validator;
4 this.user=user;
5 this.password=password;
6 }

13.实现call()方法,返回一个String类型对象。


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

14.如果用户没有通过UserValidator对象验证,写入一条信息到控制台,表明这种情况,并且抛出一个Exception异常。


1 if (!validator.validate(user, password)) {
2 System.out.printf("%s: The user has not been found\n",validator.getName());
3 throw new Exception("Error validating user");
4 }

15.否则,写入一条信息到控制台表明用户已通过验证,并返回UserValidator对象的名称。


1 System.out.printf("%s: The user has been found\n",validator.getName());
2 return validator.getName();

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


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

17.创建两个String对象,一个名为name,另一个名为password,使用”test”值初始化它们。


1 String username="test";
2 String password="test";

18.创建两个UserValidator对象,一个名为ldapValidator,另一个名为dbValidator。


1 UserValidator ldapValidator=new UserValidator("LDAP");
2 UserValidator dbValidator=new UserValidator("DataBase");

19.创建两个TaskValidator对象,分别为ldapTask和dbTask。分别使用ldapValidator和dbValidator初始化它们。


1 TaskValidator ldapTask=new TaskValidator(ldapValidator,username, password);
2 TaskValidator dbTask=new TaskValidator(dbValidator,username,password);

20.创建TaskValidator队列,添加两个已创建的对象(ldapTask和dbTask)。


1 List<TaskValidator> taskList=new ArrayList<>();
2 taskList.add(ldapTask);
3 taskList.add(dbTask);

21.使用Executors类的newCachedThreadPool()方法创建一个新的ThreadPoolExecutor对象和一个类型为String,名为result的变量。


1 ExecutorService executor=(ExecutorService)Executors.newCachedThreadPool();
2 String result;

22.调用executor对象的invokeAny()方法。该方法接收taskList参数,返回String类型。同样,它将该方法返回的String对象写入到控制台。


1 try {
2 result = executor.invokeAny(taskList);
3 System.out.printf("Main: Result: %s\n",result);
4 } catch (InterruptedException e) {
5 e.printStackTrace();
6 } catch (ExecutionException e) {
7 e.printStackTrace();
8 }

23.使用shutdown()方法结束执行者,写入一条信息到控制台,表明程序已结束。


1 executor.shutdown();
2 System.out.printf("Main: End of the Execution\n");

它是如何工作的…

Main 类是这个示例的关键。ThreadPoolExecutor类中的invokeAny()方法接收任务数列,并启动它们,返回完成时没有抛出异常的第一个 任务的结果。该方法返回的数据类型与启动任务的call()方法返回的类型一样。在本例中,它返回String值。

以下截图显示,当一个任务验证用户时,执行示例的部分输出:

3

这 个示例有两个返回随机Boolean值的UserValidator对象。每个UserValidator对象被一个实现TaskValidator类的Callable对象使用。如果UserValidator类的validate()方法返回false,TaskValidator类将抛出异常。否则,它将返回true值。

所以,我们有两个任务,可以返回true值或抛出异常。有以下4种情况:

  • 两个任务都返回ture。invokeAny()方法的结果是第一个完成任务的名称。
  • 第一个任务返回true,第二个任务抛出异常。invokeAny()方法的结果是第一个任务的名称。
  • 第一个任务抛出异常,第二个任务返回true。invokeAny()方法的结果是第二个任务的名称。
  • 两个任务都抛出异常。在本例中,invokeAny()方法抛出一个ExecutionException异常。

如果你多次运行这个示例,你可以获取以上这4种情况。

以下截图显示当两个任务抛出异常时,应用程序的输出:

4

不止这些…

ThreadPoolExecutor类提供其他版本的invokeAny()方法:

  • invokeAny(Collection<? extends Callable<T>> tasks, long timeout,TimeUnit unit):此方法执行所有任务,并返回第一个完成(未超时)且没有抛出异常的任务的结果。TimeUnit类是个枚举类,有如下常量:DAYS,HOURS,MICROSECONDS,MILLISECONDS, MINUTES,,NANOSECONDS 和SECONDS。

参见

  • 在第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

热门文章

最新文章

相关实验场景

更多