Java Callable and Future Tutorial

In this article, we will learn important two concurrency interfaces Callable and Future with examples.
One of the advantages of the Executor framework is that you can run concurrent tasks that return a result. The Java Concurrency API achieves this with the following two interfaces:
  1. Callable: This interface has the call() method. In this method, you have to implement the logic of a task. The Callable interface is a parameterized interface, meaning you have to indicate the type of data the call() method will return.
  2. Future: This interface has some methods to obtain the result generated by a Callable object and to manage its state.

What will we Learn?

1. Callable Interface
2. Future Interface
3. Executing Callable tasks using ExecutorService and obtaining the result using Future
4. Future - Check Completion of Task
5. Future - Canceling a Future
6. Future - Adding Timeouts
7. Future - invokeAll
8. Future - invokeAny

1. Callable Interface

Callable is a generic interface that is defined like this:
@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;
}
Here, V indicates the type of data returned by the task. Callable defines only one method, call( ), which is shown here:
V call( ) throws Exception
Inside call( ), you define the task that you want to perform. After that task completes, you return the result. If the result cannot be computed, call( ) must throw an exception. A Callable task is executed by an ExecutorService, by calling its submit( ) method. There are three forms of submit( ), but only one is used to execute a Callable. It is shown here:
<T> Future<T> submit(Callable<T> task)
Here, a task is the Callable object that will be executed in its own thread. The result is returned through an object of type Future.
Example:
Callable<String> callable = new Callable<String>() {
 
    @Override
    public String call() throws Exception {
       Thread.sleep(2000);
       return "Return some result";
    }
};
Note that the Callable interface has only one method so it is a functional interface and we can apply lambda expression to it as:
Callable<String> callable  = () -> {
       Thread.sleep(2000);
       return "Return some result";
};

2. Future Interface

Future is a generic interface that represents the value that will be returned by a Callable object. Because this value is obtained at some future time, the name Future is appropriate.
Future is defined like this:
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;
}
Here, V specifies the type of the result. To obtain the returned value, you will call Future’s get( ) method, which has these two forms:
V get( ) throws InterruptedException, ExecutionException
V get(long wait, TimeUnit tu) throws InterruptedException, ExecutionException, TimeoutException
The first form waits for the result indefinitely. The second form allows you to specify a timeout period in wait.

3. Executing Callable tasks using ExecutorService and obtaining the result using Future

Threads that return values are better implemented in Java using Callable and Future. Use the Executors Framework to run a Callable task.
Following is a simple example of Future and Callable. In this example, we are creating five tasks using a callable interface and each task job is to sum numbers given for each task and the result is stored in a Future interface.
Let's first create task - SumNumbers.
class SumNumbers implements Callable<Integer> {
     private int n;
 
     public SumNumbers(int n) {
          this.n = n;
     }
 
     public Integer call() {
          int sum = 0;
          for (int i = 1; i <= n; i++) {
   
              sum += i;
              try {
                   Thread.sleep(200);
              } catch (InterruptedException e) {
                   e.printStackTrace();
              }
          }
          System.out.println("[" + Thread.currentThread().getName() + "] of sum " + sum);
          return sum;
     }
}
Let's create and submit Task using ExecutorService.submit() method returns immediately and gives you a Future. Once you have obtained a future, you can execute other tasks in parallel while your submitted task is executing, and then use future.get() method to retrieve the result of the future.
public class ReturnValuesUsingCallable {
 
    public static void main(String[] args) throws InterruptedException, ExecutionException {
  
    System.out.println("Thread main started");
  
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    Future<Integer> returnedValues = executorService.submit(new SumNumbers(10));
    System.out.println("Result of Future object:: " + returnedValues.get());
    executorService.shutdown();
  
    System.out.println("Thread main finished");
  }
}
Output:
Thread main started
[pool-1-thread-1] of sum 55
Result of Future object:: 55
Thread main finished
Let's simplify above example using Java 8 Lambda Expressions:
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ReturnValuesUsingCallable {
 
    public static void main(String[] args) throws InterruptedException, ExecutionException {
  
        System.out.println("Thread main started");
  
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future<Integer> returnedValues = executorService.submit(() -> {
             int sum = 0;
             for (int i = 1; i <= 5; i++) {
    
                 sum += i;
                 try {
                      Thread.sleep(200);
                 } catch (InterruptedException e) {
                      e.printStackTrace();
                 }
              }
            System.out.println("[" + Thread.currentThread().getName() + "] of sum " + sum);
            return sum;
       });
  
       System.out.println("Result of Future object:: " + returnedValues.get());
       executorService.shutdown();
  
       System.out.println("Thread main finished");
   }
}
Output:
Thread main started
[pool-1-thread-1] of sum 15
Result of Future object:: 15
Thread main finished
Note that, the get() method blocks until the task is completed. The Future API also provides an isDone() method to check whether the task is completed or not.

4. Check Completion of Task

Returns true if this task completed. Completion may be due to normal termination, an exception, or cancellation -- in all of these cases, this method will return true.
public class ReturnValuesUsingCallable {
 
     public static void main(String[] args) throws InterruptedException, ExecutionException {
  
         System.out.println("Thread main started");
  
         ExecutorService executorService = Executors.newSingleThreadExecutor();
         Future<Integer> returnedValues = executorService.submit(() -> {
             int sum = 0;
             for (int i = 1; i <= 5; i++) {
                  sum += i;
                  try {
                        Thread.sleep(200);
                  } catch (InterruptedException e) {
                        e.printStackTrace();
                  }
             }
           System.out.println("[" + Thread.currentThread().getName() + "] of sum " + sum);
           return sum;
       });
  
       while(!returnedValues.isDone()) {
             System.out.println("Task is still not done...");
             Thread.sleep(200);
       }
   
       System.out.println("Result of Future object:: " + returnedValues.get());
       executorService.shutdown();
  
       System.out.println("Thread main finished");
    }
}
Output:
Thread main started
Task is still not done...
Task is still not done...
Task is still not done...
Task is still not done...
Task is still not done...
Task is still not done...
[pool-1-thread-1] of sum 15
Result of Future object:: 15
Thread main finished

5. Canceling a Future

You can cancel a future using Future.cancel() method. It attempts to cancel the execution of the task and returns true if it is canceled successfully, otherwise, it returns false.
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class ReturnValuesUsingCallable {
 
     public static void main(String[] args) throws InterruptedException, ExecutionException {
  
         System.out.println("Thread main started");
  
         ExecutorService executorService = Executors.newSingleThreadExecutor();
         Future<Integer> returnedValues = executorService.submit(() -> {
             int sum = 0;
             for (int i = 1; i <= 5; i++) {
                  sum += i;
                  try {
                        Thread.sleep(200);
                  } catch (InterruptedException e) {
                        e.printStackTrace();
                  }
             }
           System.out.println("[" + Thread.currentThread().getName() + "] of sum " + sum);
           return sum;
       });
  
       while(!returnedValues.isDone()) {
             System.out.println("Task is still not done...");
             Thread.sleep(200); returnedValues.cancel(true);
       }
   
       System.out.println("Result of Future object:: " + returnedValues.get());
       executorService.shutdown();
  
       System.out.println("Thread main finished");
    }
}
Output:
Thread main started
Task is still not done...
[pool-1-thread-1] of sum 15
Exception in thread "main" java.util.concurrent.CancellationException
 at java.util.concurrent.FutureTask.report(FutureTask.java:121)
 at java.util.concurrent.FutureTask.get(FutureTask.java:192)
 at com.javaguides.javamultithreading.concurrency.ReturnValuesUsingCallable
        .main(ReturnValuesUsingCallable.java:32)

6. Adding Timeouts

The future.get() method blocks and waits for the task to complete. If you call an API from a remote service in the callable task and the remote service is down, then future.get() will block forever, which will make the application unresponsive.
To guard against this fact, you can add a timeout in the get() method -
future.get(1, TimeUnit.SECONDS);
The future.get() method will throw a TimeoutException if the task is not completed within the specified time.

7. invokeAll

Submit multiple tasks and wait for all of them to complete.
You can execute multiple tasks by passing a collection of Callables to the invokeAll() method. The invokeAll() returns a list of Futures. Any call to future.get() will block until all the Futures are complete.
import java.util.Arrays;
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;

public class ReturnValuesUsingCallable {
 
    public static void main(String[] args) throws InterruptedException, ExecutionException {
  
        System.out.println("Thread main started");
  
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        List<Future<Integer>> returnedValues = executorService.invokeAll(Arrays.asList(
            new SumNumbers(50), 
            new SumNumbers(40),
            new SumNumbers(30),
            new SumNumbers(20),
            new SumNumbers(10)));
  
           for (Future<Integer> value : returnedValues) {
                System.out.println(value.get());
           }
  
           executorService.shutdown();
  
           System.out.println("Thread main finished");
     }
}

class SumNumbers implements Callable<Integer> {
      private int n;
 
      public SumNumbers(int n) {
           this.n = n;
      }
 
      public Integer call() {
         int sum = 0;
         for (int i = 1; i <= n; i++) {
              sum += i;
              try {
                  Thread.sleep(200);
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
         }
         System.out.println("[" + Thread.currentThread().getName() + "] Sum " + sum);
         return sum;
      }
}
Output:
Thread main started
[pool-1-thread-5] Sum 55
[pool-1-thread-4] Sum 210
[pool-1-thread-3] Sum 465
[pool-1-thread-2] Sum 820
[pool-1-thread-1] Sum 1275
1275
820
465
210
55
Thread main finished

8. invokeAny

Executes the given tasks, returning the result of one that has completed successfully (i.e., without throwing an exception), if any do. Upon normal or exceptional return, tasks that have not completed are canceled. The results of this method are undefined if the given collection is modified while this operation is in progress.
import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ReturnValuesUsingCallable {
 
     public static void main(String[] args) throws InterruptedException, ExecutionException {
  
          System.out.println("Thread main started");
  
          ExecutorService executorService = Executors.newFixedThreadPool(5);
          Integer returnedValues = executorService.invokeAny(Arrays.asList(
          new SumNumbers(50), 
          new SumNumbers(40),
          new SumNumbers(30),
          new SumNumbers(20),
          new SumNumbers(10)));
  
          System.out.println(returnedValues);
  
          executorService.shutdown();
  
          System.out.println("Thread main finished");
      }
}

class SumNumbers implements Callable<Integer> {
     private int n;
 
     public SumNumbers(int n) {
         this.n = n;
     }
 
     public Integer call() {
         int sum = 0;
         for (int i = 1; i <= n; i++) {
              sum += i;
         }
         System.out.println("[" + Thread.currentThread().getName() + "] Sum " + sum);
         return sum;
     }
}
Output:
Thread main started
[pool-1-thread-1] Sum 1275
1275
Thread main finished
Learn Multithreading on Java Multithreading Tutorial
Learn advanced Java concurrency framework on  Java Concurrency Tutorial

Comments