Научитесь использовать метод 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 является шаблонным классом и на основе исходного значения подключается либо к базе данных, либо к 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 credentialstry {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. Экземпляры этого класса могут быть отправлены в службу исполнителя для запуска.
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;}@Overridepublic 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 = "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 resultexecutor.shutdown();}catch(InterruptedException e){e.printStackTrace();}catch(ExecutionException e) {e.printStackTrace();}}}
Вывод программы.
LDAP : validating a user in 6 secondsDataBase : validating a user in 3 secondsDataBase: The user has been foundUser has been validated from : DataBase
4. Заключение
В примере выше мы научились выполнять две вызываемые задачи в службе исполнителя. Мы научились выполнять все задачи за один раз, используя метод invokeAny(), который возвращает первый доступный результат.
Мы также рассмотрели случаи, когда метод invokeAny() весьма полезен.
Пишите мне свои вопросы в разделе комментариев.