ExecutorService invokeAny() - 运行多个任务并处理第一个结果

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(301)   2023-06-26 07:54:18

学习使用 ExecutorService.invokeAny(tasks) 方法,我们同时执行多个任务,但我们会在其中任何一个任务完成时做出决定并返回结果。

1.invokeAny()方法

此方法执行给定的任务列表,返回已成功完成(即未抛出异常)的结果(如果有的话)。

这是一个重载方法。第二种方法采用超时参数,并在给定的超时结束之前返回,无论任务是否完成。

<T> T 	invokeAny(Collection<? extends Callable<T>> tasks);

<T> T 	invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit);

2. ExecutorService invokeAny() 用例

如果我们有各种并发任务可用于解决给定问题,但我们只对第一个结果感兴趣,我们可以使用 invokeAny() 方法。例如,

  • 我们在应用程序中有多个数组排序算法,我们可以接受排序最快并返回排序后数组的任何算法。
  • 我们有多种方法来验证用户身份,只要任何一种方法成功验证用户身份,我们就会返回成功响应。

3. ExecutorService invokeAny() 例子

在这个例子中,我们有两种方法来验证用户的详细信息,即数据库和 LDAP。我们将在不同的任务中同时调用这两种方法。一旦我们能够通过任何给定方法对用户进行身份验证,我们就会断定用户已通过身份验证。

UserValidator 类是模板类,基于源值,它连接到 DB 或 LDAP 并验证用户。

import java.util.concurrent.TimeUnit;

public class UserValidator 
{
	private final String source;
	
	public String getSource() {
		return source;
	}

	public UserValidator(String source) {
		this.source = source;
	}

	public boolean validate(String name, String password) 
	{
		//Connect to backend based on source value 
		//and validate the credentials
		
		try {
			long duration = (long) (Math.random() * 10);
			System.out.printf("%s : validating a user in %d seconds\n", this.source, duration);
			TimeUnit.SECONDS.sleep(duration);
		} catch (InterruptedException e) {
			return false;
		}
		return true;
	}
}

UserValidatorTask 类代表一个实现Callable 接口的验证任务。这个类的实例可以提交给executor service运行。

import java.util.concurrent.Callable;

public class UserValidatorTask implements Callable<String> 
{
	private final UserValidator validator;
	private final String user;
	private final String password;

	public UserValidatorTask(UserValidator validator, String user, String password) {
		this.validator = validator;
		this.user = user;
		this.password = password;
	}

	@Override
	public String call() throws Exception {
		if (!validator.validate(user, password)) {
			throw new Exception("Error validating user");
		}
		System.out.printf("%s: The user has been found\n", validator.getSource());
		return validator.getSource();
	}
}

最后,Main 类具有创建验证任务的实际逻辑,提交给执行程序服务,然后验证从给定的两个任务中的任何一个获得的第一个结果。

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Main 
{
	public static void main(String[] args) throws InterruptedException 
	{
		String username = "cundage";
		String password = "password";
		String result;

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

		UserValidatorTask ldapTask = new UserValidatorTask(ldapValidator, username, password);
		UserValidatorTask dbTask = new UserValidatorTask(dbValidator, username, password);

		List<UserValidatorTask> taskList = new ArrayList<>();
		taskList.add(ldapTask);
		taskList.add(dbTask);

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

		try 
		{
			result = executor.invokeAny(taskList);
			System.out.printf("User has been validated from : %s\n", result);
			
			//We may cancel all pending 
                        //tasks after we have our result
			executor.shutdown(); 
		} 
		catch (InterruptedException e) 
		{
			e.printStackTrace();
		} 
		catch (ExecutionException e) {
			e.printStackTrace();
		}
	}
}

程序输出。

LDAP : validating a user in 6 seconds
DataBase : validating a user in 3 seconds

DataBase: The user has been found
User has been validated from : DataBase

4.总结

在上面的例子中,我们学会了在执行器服务中执行两个可调用任务。我们学会了使用返回第一个可用结果的 invokeAny() 方法一次性执行所有任务。

我们还看到了 invokeAny() 方法非常有用的用例。

在评论部分把你的问题告诉我。

快乐学习!!

地址:https://www.cundage.com/article/executorservice-invokeany.html

相关阅读

使用 RejectedExecutionHandler 学习处理提交给 Executor 并由于执行器因任何原因关闭而被拒绝的任务。 1. 当任务被拒绝时 请记住,当我们完成执行器的执行时,我们...
如果您使用的是纯 Java,从版本 5 开始,我们有一个方便的调度程序类,允许以固定速率或固定延迟运行任务: import java.util.concurrent.Executors; imp...
根据 java 文档,CountDownLatch 是一种同步辅助工具,它允许一个或多个线程等待,直到其他线程中执行的一组操作完成。 CountDownLatch 概念是 java concur...
学习使用 ExecutorService.invokeAny(tasks) 方法,我们同时执行多个任务,但我们会在其中任何一个任务完成时做出决定并返回结果。 1.invokeAny()方法 此方...
在之前的教程中,我们了解了基本线程池执行器,线程池中的线程数量是无限的,它是示例用法。现在让我们看一下固定大小线程池执行器的示例,这将有助于通过限制线程池中的最大线程数来提高性能和更好地利用系统...
我们已经知道关于 线程同步 以及使用同步 关键字的各种机制。 Java 提供了另一种基于 Lock 接口和实现它的类(例如 ReentrantLock).在本教程中,我们将看到 Lock 接口的...
Java 并发允许在不同的线程中运行一个任务的多个子任务。有时,有必要等待所有线程执行完毕。在本教程中,我们将学习几种使当前线程等待其他线程完成的方法。 1. 使用 ExecutorServic...
我们的一位读者 Anant 问了这个非常好的问题,详细说明/列出了我们应该了解的关于多线程的所有相关主题,包括 Java 8 中所做的更改。(初级到高级)。他只想知道 Java 中多线程框架的演...
Java Future 教程展示了如何使用 Future 在 Java 中进行异步编程。 Future 表示异步计算的结果。提供了检查计算是否完成、等待其完成以及检索计算结果的方法。简单来说,F...
在 JDK 8 之前,无法在 Java 中创建大型、线程安全的 ConcurrentHashSet。 java.util.concurrent 包甚至没有名为 ConcurrentHashSet...