ExecutorService invokeAny()

Learn to use ExecutorService.invokeAny(tasks) method where we execute multiple tasks at same time, but we make a decision when any one of those tasks is completed and return it’s result.

1. invokeAny() method

This method executes the given list of tasks, returning the result of one that has completed successfully (i.e., without throwing an exception), if any do.

It is an overloaded method. Second method takes timeout parameter and returns before the given timeout elapses whether any task is completed or not.

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

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

2. ExecutorService invokeAny() usecases

We can use invokeAny() method in cases where we have various concurrent tasks available to solve a given problem, but we are only interested in the first result. For example,

  • We have multiple array sorting algorithms in application, and we are OK with anyone which sorts fastest and return the sorted array.
  • We have multiple ways to authenticate a user’s identity and we shall return a success response as soon as any one method is successfully able to authenticate the user.

3. ExecutorService invokeAny() example

In this example, we have two ways to authenticate a user’s detail i.e. database and LDAP. We will invoke both methods in separate tasks at same time. As soon as we are able to authenticate user from any given method, we will conclude the user is authenticated.

UserValidator class is template class and based on source value, it connects to either DB or LDAP and validate the user.

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 class represent a validation task which implements Callable interface. Instances of this class can be submitted to executor service to run.

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();
	}
}

Finally, Main class has the actual logic for creating the validation tasks, submitting to executor service and then validate the first result obtained from any of given two tasks.

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 = "howtodoinjava";
		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();
		}
	}
}

Program output.

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. Conclusion

In above example, we learned to execute two callable tasks in executor service. We learned to execute all tasks in one go using invokeAny() method which returns the first available result.

We also saw the usecases where invokeAny() method is pretty useful.

Drop me your questions in comments section.

Happy Learning !!

Comments

Subscribe
Notify of
guest
1 Comment
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode