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

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

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

在这个指南中,你将学习如何使用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);
4TimeUnit.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,
2String 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<>();
2taskList.add(ldapTask);
3taskList.add(dbTask);

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


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

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


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

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


1executor.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章,线程执行者中的运行多个任务并处理所有结果指南
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在多线程应用程序中,委托消息是一种常用的模式。它可以用于在不同的线程之间传递消息或执行任务。 举个例子,假设有一个 GUI 程序,它有一个主线程负责渲染用户界面和响应用户交互,还有一个后台线程负责处理耗时的任务。在这种情况下,后台线程可能需要在完成任务时通知主线程,更新 UI 或显示消息。这时,我们就可以使用委托消息来实现这一目的。 具体来说,我们可以在主线程中定义一个委托,并在后台线程中调用该委托来传递消息或执行任务。这样,我们就可以在不同的线程之间安全地传递消息或执行任务,而不会出现线程安全问题。 例如,我们可以这样定义一个委托: ``` delegate void TaskCompletedHandler(string result); ``` 然后,在后台线程中调用该委托: ``` TaskCompletedHandler handler = new TaskCompletedHandler(UpdateUI); handler("Task completed successfully"); ``` 在这里,`UpdateUI` 是一个在主线程中定义的方法,用于更新 UI 或显示消息。通过调用委托,我们就可以在后台线程中安全地通知 ### 回答2: 编写一个多线程的委托消息应用场景可以是一个网络聊天应用程序。这个应用程序允许多个用户同时登录和发送消息。 首先,创建一个Server类,它负责监听来自客户端的连接请求。当一个新的客户端连接成功后,Server会将这个新的连接分配给一个独立的线程处理。 每个客户端连接都会创建一个对应的Client类,它负责接收和发送消息。当Client接收到一个消息时,它会通过事件委托方式通知Server。这可以通过定义一个委托类型和事件来实现。当有新的消息到达时,Server的消息处理器会被触发。 Server的消息处理器首先会检查消息的发送者和接收者是否合法。如果合法,消息会被转发给对应的接收者。这个转发的过程可以利用多线程来实现,每个消息在一个新的线程处理。这样可以避免某个消息的处理耗时导致其他消息的延迟。 同时,Server可以维护一个在线用户列表,用于跟踪当前在线的用户。当一个用户退出聊天时,对应的Client会发送一个离线消息,Server会将该用户从在线列表中移除。 除了上述的基本功能,还可以添加更多的功能,例如群聊、私聊、文件传输等。在接收到相应请求时,Server会将消息委托到相应的处理器来处理。这些处理器也可以利用多线程来提高并发处理能力。 总之,通过使用多线程和委托消息的设计,可以实现一个高效、稳定的多用户聊天应用。每个用户在独立的线程处理消息,避免了阻塞,并且通过委托机制实现了消息的灵活处理和扩展。 ### 回答3: 多线程的委托消息应用场景是在需要多个线程并行执行任务,并且这些任务需要相互通信和协调的情况下。下面以一个简单的电商应用场景为例进行说明。 假设有一个电商平台,用户可以在平台上购买商品。平台需要实时更新各个商品的库存数量,并且需要给用户发送购买成功消息和更新库存消息。 在这个场景中,可以使用多线程的委托消息模式来并行执行如下几个任务: 1. 订单处理:当用户下单购买商品后,平台需要创建一个订单并返回给用户一个购买成功的消息。这个任务可以由一个线程处理,当订单创建完毕后,将订单对象传递给库存更新任务。 2. 库存更新:在用户购买成功后,需要更新对应商品的库存数量。这个任务可以由另一个线程处理,当库存更新完毕后,将更新后的库存数量传递给发送消息任务。 3. 发送消息:在用户购买成功后,需要给用户发送购买成功的消息。这个任务可以由第三个线程处理,当发送消息完毕后,任务完成。 在这个场景中,使用多线程的委托消息模式可以实现并行执行任务的效果,提高系统的响应速度和并发处理能力。不同的任务通过委托和传递消息的方式,实现了任务之间的解耦合,提高了系统的扩展性和可维护性。 需要注意的是,在编写多线程的委托消息应用场景时,需要考虑线程安全、消息传递的正确性和效率等问题,使用合适的同步机制和线程间通信方式来保证程序的正确性和性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值