Learn to execute callable tasks (which return a result of type Future after execution) using ExecutorService implementations in this simple Callable Future example.
1. Important interfaces
1.1. Callable
In Java concurrency, Callable represents a task that returns a result. Executor can run callable tasks – concurrently.
Since Java 8, it is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.
@FunctionalInterface public interface Callable<V> { /** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */ V call() throws Exception; }
1.2. Future
Future interface has methods to obtain the result generated by a Callable
object and manage its state. It represents the result of an asynchronous computation.
The result can only be retrieved using method get()
when the computation has completed, blocking if necessary until it is ready.
Cancellation of a task is performed by the cancel()
method. Once a computation has completed, the computation cannot be cancelled.
public interface Future<V> { boolean cancel(boolean mayInterruptIfRunning); boolean isCancelled(); boolean isDone(); V get() throws InterruptedException, ExecutionException; V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException; }
2. Callable Future Example
In this example, we are creating a task which will return the factorial of a given number. The return value of the task after it’s completion is the factorial of argument number.
class FactorialCalculator implements Callable<Integer> { private final Integer number; public FactorialCalculator(Integer number) { this.number = number; } @Override public Integer call() throws Exception { int result = 1; if ((number == 0) || (number == 1)) { result = 1; } else { for (int i = 2; i <= number; i++) { result *= i; TimeUnit.MILLISECONDS.sleep(20); } } System.out.printf("Factorial of %d is :: %d\n", number, result); return result; } }
Now when the calculator is ready, we will use ExecutorService to execute the tasks. Here we will create 10 tasks which will compute factorial of numbers 1 to 10.
Each task will be submitted to ExecutorService
internally managing thread pool of 2 threads.
We will use method awaitTermination() to wait for all tasks to complete before checking the output of all tasks.It is optional step, and we are allowed not to wait and start checking the outputs as an when a task is complete.
import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class Main { public static void main(String[] args) throws InterruptedException { ExecutorService executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2); List<Future<Integer>> resultList = new ArrayList<>(); for (int i = 1; i <= 10; i++) { FactorialCalculator calculator = new FactorialCalculator(i); Future<Integer> result = executor.submit(calculator); resultList.add(result); } executor.awaitTermination(5, TimeUnit.SECONDS); for (int i = 0; i < resultList.size(); i++) { Future<Integer> result = resultList.get(i); Integer number = null; try { number = result.get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } System.out.printf("Main: Task %d: %d\n", i, number); } executor.shutdown(); } }
Program output.
Factorial of 1 is :: 1 Factorial of 2 is :: 2 Factorial of 3 is :: 6 Factorial of 4 is :: 24 Factorial of 5 is :: 120 Factorial of 6 is :: 720 Factorial of 7 is :: 5040 Factorial of 8 is :: 40320 Factorial of 9 is :: 362880 Factorial of 10 is :: 3628800 Main: Task 0: 1 Main: Task 1: 2 Main: Task 2: 6 Main: Task 3: 24 Main: Task 4: 120 Main: Task 5: 720 Main: Task 6: 5040 Main: Task 7: 40320 Main: Task 8: 362880 Main: Task 9: 3628800
In above example, when we send callable object to be executed in an executor using the submit()
method, it returns a Future
object that you can use with two main objectives:
3. Conclusion
Above example demonstrate that –
- We can control the status of the task i.e. we can cancel the task after checking whether it has finished or not. For this purpose, we can use the isDone() method.
- We can get the result returned using
get()
method. This method waits until the Callable object has finished the execution of the call() method and has returned its result. - If the thread is interrupted while the
get()
method is waiting for the result, it throws anInterruptedException
exception. - If the
call()
method throws an exception, then theget()
method throws anExecutionException
exception as well.
Drop me your questions related to Java ExecutorService Callable Example in comments.
Happy Learning !!