Java Concurrency of Executor (return result processing) run multiple tasks and process the first result run multiple tasks and process all results
A common problem with concurrent programming is that when multiple concurrent tasks are used to solve a problem, we tend to be interested in only the first result returned. For example, there are multiple sorting algorithms for an array, and it is possible to start all of them concurrently, but for a given array, the first algorithm that gets the sorted result is the fastest sorting algorithm.
We go through an instance which initiates two verification tasks, and as soon as one of them passes, it passes.
The class that implements the validation process has a simple logic that randomly validates whatever the username is, returning a random boolean.
package CreateExcutorInvokeAny; import java.util.Random; import java.util.concurrent.TimeUnit; public class UserValidator { private String name; public UserValidator (String name) { this.name = name; } public boolean validate(String name,String password) { Random random = new Random(); Long duration = (long)Math.random()*10; System.out.printf("Validator %s : Validator a user during %d seconds ", this.name, duration); try { TimeUnit.SECONDS.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); return false; } return random.nextBoolean(); } public String getName() { return this.name; } }
Callable object, whose logic is to return the result if the validation passes, and to throw an exception if the validation does not pass.
package CreateExcutorInvokeAny; import java.util.concurrent.Callable; import java.util.concurrent.Future; public class TaskValidator implements Callable<String> { private UserValidator validator; private String user; private String password; public TaskValidator(UserValidator validator, String user, String password) { super(); this.validator = validator; this.user = user; this.password = password; } @Override public String call() throws Exception { if(!validator.validate(user, password)) { System.out.println(validator.getName() + "the user has not been found"); throw new Exception("Error validating user"); } System.out.println(validator.getName() + "has found"); return validator.getName(); } }
Main class
package CreateExcutorInvokeAny; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { String username = "test"; String password = "test"; UserValidator oneValidator = new UserValidator("one"); UserValidator twoValidator = new UserValidator("two"); TaskValidator oneTask = new TaskValidator(oneValidator, username, password); TaskValidator twoTask = new TaskValidator(twoValidator, username, password); List<TaskValidator> taskList = new ArrayList<>(); taskList.add(oneTask); taskList.add(twoTask); ExecutorService executor = Executors.newCachedThreadPool(); String res; try { res = executor.invokeAny(taskList); System.out.println("Main : res : " + res); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } executor.shutdown(); System.out.println("Main : end of the execution"); } }
The key step here is the method invokeAny, which returns the result of the first task that finished execution, that is, if the validation did not pass and the task could not be executed to completion, naturally it will not complete and will not return, if the validation passed, it will return the result.
Our analysis of the procedure leads to four possibilities.
image.png
image.png
image.png
image.png
Executor allows concurrent tasks to be executed without having to think about thread creation and execution If you want to wait for a thread to finish, there are two ways to do it.
All tasks can be executed using the invokeall method, which will wait until all tasks have been executed and then return.
Let's look at an example.
package CreateExecutorInvokeAll; import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; public class Task implements Callable<Result> { private String name; public Task(String name) { this.name = name; } @Override public Result call() throws Exception { System.out.println(this.name + " Starting "); Long duration = (long)Math.random()*10; System.out.printf("Validator %s : Validator a user during %d seconds ", this.name, duration); try { TimeUnit.SECONDS.sleep(duration); } catch (InterruptedException e) { e.printStackTrace(); } int value = 0; for(int i=0;i<5;i++) { value += (int)Math.random()*100; } Result res = new Result(); res.setName(this.name); res.setValue(value); System.out.println(this.name + " end"); return res; } }
package CreateExecutorInvokeAll; public class Result { private String name; private int value; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getValue() { return value; } public void setValue(int value) { this.value = value; } }
package CreateExecutorInvokeAll; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Main { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); List<Task> tasklist = new ArrayList<>(); for(int i=0;i<3;i++) { Task task = new Task(String.valueOf(i)); tasklist.add(task); } List<Future<Result>> reslist = new ArrayList<>(); try { reslist = executor.invokeAll(tasklist); } catch (InterruptedException e) { e.printStackTrace(); } executor.shutdown(); System.out.println("Main : res"); for(int i=0;i<reslist.size();i++) { Future<Result> future = reslist.get(i); try { Result res = future.get(); System.out.println("result : " + res.getName() + "||" + res.getValue()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } } }
running result
image.png