The Executor framework allows you to execute concurrent tasks without worrying about thread creation and execution. It provides you the Future class that you can use to control the status and get the results of any task executed in an executor.
When you want to wait for the finalization of a task, you can use the following two methods:
f The isDone() method of the Future interface returns true if the task has finished its execution.
f The awaitTermination() method of the ThreadPoolExecutor class puts the thread to sleep until all the tasks have finished their execution after a call to the shutdown() method.
These two methods have some drawbacks. With the first one, you can only control the completion of a task, and with the second one, you have to shutdown the executor to wait for a thread, otherwise the method's call returns immediately.
The ThreadPoolExecutor class provides a method that allows you to send to the executor a list of tasks and wait for the finalization of all the tasks in the list. In this recipe, you will learn how to use this feature by implementing an example with three tasks executed and their results printed out when they finish.
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 Result to store the results generated in the concurrent tasks of this example.
public class Result {
2. Declare two private attributes. One String attribute named name and one int attribute named value.
private String name;
private int value;
3. Implement the corresponding get() and set() methods to set and return the value of the name and value attributes.
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;
}
4. Create a class named Task that implements the Callable interface parameterized with the Result class.
public class Task implements Callable<Result> { 5. Declare a private String attribute named name.
private String name;
6. Implement the constructor of the class that initializes its attribute.
public Task(String name) { this.name=name;
}
7. Implement the call() method of the class. In this case, this method will return a Result object.
@Override
public Result call() throws Exception {
8. First, write a message to the console to indicate that the task is starting.
System.out.printf("%s: Staring\n",this.name);
9. Then, wait for a random period of time.
try {
long duration=(long)(Math.random()*10);
System.out.printf("%s: Waiting %d seconds for results.\
n",this.name,duration);
TimeUnit.SECONDS.sleep(duration);
} catch (InterruptedException e) { e.printStackTrace();
}
10. To generate an int value to return in the Result object, calculate the sum of five random numbers.
int value=0;
for (int i=0; i<5; i++){
value+=(int)(Math.random()*100);
11. Create a Result object and initialize it with the name of this task and the result of the operation done earlier.
Result result=new Result();
result.setName(this.name);
result.setValue(value);
12. Write a message to the console to indicate that the task has finished.
System.out.println(this.name+": Ends");
13. Return the Result object.
return result;
}
14. Finally, 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) {
15. Create a ThreadPoolExecutor object using the newCachedThreadPool() method of the Executors class.
ExecutorService executor=(ExecutorService)Executors.
newCachedThreadPool();
16. Create a list of Task objects. Create three Task objects and save them on that list.
List<Task> taskList=new ArrayList<>();
for (int i=0; i<3; i++){
Task task=new Task(i);
taskList.add(task);
}
17. Create a list of Future objects. These objects are parameterized with the Result class.
List<Future<Result>>resultList=null;
18. Call the invokeAll() method of the ThreadPoolExecutor class. This class will return the list of the Future objects created earlier.
try {
resultList=executor.invokeAll(taskList);
} catch (InterruptedException e) { e.printStackTrace();
}
19. Finalize the executor using the shutdown() method.
executor.shutdown();
20. Write the results of the tasks processing the list of the Future objects.
System.out.println("Main: Printing the results");
for (int i=0; i<resultList.size(); i++){
Future<Result> future=resultList.get(i);
try {
Result result=future.get();
System.out.println(result.getName()+": "+result.
getValue());
} catch (InterruptedException | ExecutionException e) { e.printStackTrace();
} }
How it works...
In this recipe, you have learned how to send a list of tasks to an executor and wait for the finalization of all of them using the invokeAll() method. This method receives a list of the Callable objects and returns a list of the Future objects. This list will have a Future object per task in the list. The first object in the list of the Future objects will be the object that controls the first task in the list of the Callable objects, and so on.
The first point to take into consideration is that the type of data used for the parameterization of the Future interface in the declaration of the list that stores the result objects must be compatible with the one used to parameterized the Callable objects. In this case, you have used the same type of data: the Result class.
Another important point about the invokeAll() method is that you will use the Future objects only to get the results of the tasks. As the method finishes when all the tasks have finished, if you call the isDone() method of the Future objects that is returned, all the calls will return the true value.
There's more...
The ExecutorService class provides another version of the invokeAll() method:
f invokeAll(Collection<?extendsCallable<T>>tasks,longtimeout, TimeUnitunit): This method executes all the tasks and returns the result of their execution when all of them have finished, if they finish before the given timeout
See also
f The Executing tasks in an executor that returns a result recipe in Chapter 4, Thread Executors
f The Running multiple tasks and processing the first result recipe in Chapter 4, Thread Executors