A common problem in concurrent programming is when you have various concurrent tasks that solve a problem, and you are only interested in the first result of those tasks. For example, you want to sort an array. You have various sort algorithms. You can launch all of them and get the result of the first one that sorts these, that is, the fastest sorting algorithm for a given array.
In this recipe, you will learn how to implement this scenario using the ThreadPoolExecutor class. You are going to implement an example where a user can be validated by two
mechanisms. The user will be validated if one of those mechanisms validates it.
Getting ready
The example of this recipe has been implemented using the Eclipse IDE. If you use Eclipse or other IDE such as NetBeans, open it and create a new Java project.
How to do it...
Follow these steps to implement the example:
1. Create a class named UserValidator that will implement the process of user validation.
public class UserValidator {
2. Declare a private String attribute named name that will store the name of a user validation system.
private String name;
3. Implement the constructor of the class that initializes its attributes.
public UserValidator(String name) { this.name=name;
}
4. Implement the validate() method. It receives two String parameters with the name and the password of the user you want to validate.
public boolean validate(String name, String password) { 5. Create a Random object named random.
Random random=new Random();
6. Wait for a random period of time to simulate the process of user validation.
try {
long duration=(long)(Math.random()*10);
System.out.printf("Validator %s: Validating a user during %d seconds\n",this.name,duration);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) { return false;
}
7. Return a random Boolean value. The method returns a true value when the user is validated and a false value when the user is not validated.
return random.nextBoolean();
}
8. Implement the getName() method. This method returns the value of the name attribute.
9. Now, create a class named TaskValidator that will execute a validation process with a UserValidation object as a concurrent task. Specify that it implements the Callable interface parameterized with the String class.
public class TaskValidator implements Callable<String> { 10. Declare a private UserValidator attribute named validator.
private UserValidator validator;
11. Declare two private String attributes named user and password. private String user;
private String password;
12. Implement the constructor of the class that will initialize all the attributes.
public TaskValidator(UserValidator validator, String user, String password){
this.validator=validator;
this.user=user;
this.password=password;
}
13. Implement the call() method that will return a String object.
@Override
public String call() throws Exception {
14. If the user is not validated by the UserValidator object, write a message to the console indicating this circumstance and throw an Exception exception.
if (!validator.validate(user, password)) {
System.out.printf("%s: The user has not been found\
n",validator.getName());
throw new Exception("Error validating user");
}
15. Otherwise, write a message to the console indicating that the user has been validated and return the name of the UserValidator object.
System.out.printf("%s: The user has been found\n",validator.
getName());
return validator.getName();
16. Now, implement the main class of the example by creating a class named Main and add the main() method to it.
public class Main {
public static void main(String[] args) {
17. Create two String objects named user and password and initialize them with the test value.
String username="test";
String password="test";
18. Create two UserValidator objects named ldapValidator and dbValidator. UserValidator ldapValidator=new UserValidator("LDAP");
UserValidator dbValidator=new UserValidator("DataBase");
19. Create two TaskValidator objects named ldapTask and dbTask. Initialize them with ldapValidator and dbValidator respectively.
TaskValidator ldapTask=new TaskValidator(ldapValidator, username, password);
TaskValidator dbTask=new TaskValidator(dbValidator,username,pa ssword);
20. Create a list of TaskValidator objects and add to it the two objects that you have created.
List<TaskValidator> taskList=new ArrayList<>();
taskList.add(ldapTask);
taskList.add(dbTask);
21. Create a new ThreadPoolExecutor object using the newCachedThreadPool() method of the Executors class and a String object named result.
ExecutorService executor=(ExecutorService)Executors.
newCachedThreadPool();
String result;
22. Call the invokeAny() method of the executor object. This method receives taskList as a parameter and returns String. Also, it writes the String object returned by this method to the console.
try {
result = executor.invokeAny(taskList);
System.out.printf("Main: Result: %s\n",result);
} catch (InterruptedException e) { e.printStackTrace();
} catch (ExecutionException e) { e.printStackTrace();
}
23. Terminate the executor using the shutdown() method and write a message to the console to indicate that the program has ended.
How it works...
The key of the example is in the Main class. The invokeAny() method of the
ThreadPoolExecutor class receives a list of tasks, launches them, and returns the result of the first task that finishes without throwing an exception. This method returns the same data type that the call() method of the tasks you launch returns. In this case, it returns a String value.
The following screenshot shows the output of an execution of the example when one task validates the user:
The example has two UserValidator objects that return a random boolean value. Each UserValidator object is used by a Callable object, implemented by the TaskValidator class. If the validate() method of the UserValidator class returns a false value, the TaskValidator class throws Exception. Otherwise, it returns the true value.
So, we have two tasks that can return the true value or throw an Exception exception.
You can have the following four possibilities:
f Both tasks return the true value. The result of the invokeAny() method is the name of the task that finishes in the first place.
f The first task returns the true value and the second one throws Exception. The result of the invokeAny() method is the name of the first task.
f The first task throws Exception and the second one returns the true value.
The result of the invokeAny() method is the name of the second task.
f Both tasks throw Exception. In that class, the invokeAny() method throws an ExecutionException exception.
If you run the examples several times, you get the four possible solutions you can get.
The following screenshot shows the output of the application when both tasks throw an exception:
There's more…
The ThreadPoolExecutor class provides another version of the invokeAny() method:
f invokeAny(Collection<?extendsCallable<T>>tasks,longtimeout, TimeUnitunit): This method executes all the tasks and returns the result of the first one that finishes without throwing an exception, if it finishes before the given timeout passes. The TimeUnit class is an enumeration with the following constants:
DAYS, HOURS, MICROSECONDS, MILLISECONDS, MINUTES, NANOSECONDS, and SECONDS.
See also
f The Running multiple tasks and processing all the results recipe in Chapter 4, Thread Executors