One of the benefits of the Java executor framework is that we can run concurrent tasks that may return a single result after processing the tasks. The Java Concurrency API achieves this with the following two interfaces Callable
and Future
.
In this tutorial, we will learn to execute Callable tasks (which return a result of type Future after execution) using ExecutorService implementations in this simple Callable Future example.
1. Java Callable and Future Interfaces
1.1. Callable
In Java concurrency, Callable represents a task that returns a result. Executors 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.
Callable
interface has the call()
method. In this method, we have to implement the logic of a task. The Callable
interface is a parameterized interface, meaning we have to indicate the type of data the call()
method will return.
@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;
}
2.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 been completed, blocking if necessary until it is ready.
The cancellation of a task is performed by the cancel()
method. Once a computation has been completed, the computation cannot be canceled.
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. Java Callable Future Example
In this example, we are executing a task that returns its name when it’s completed. We will use ExecutorService to execute this task and print its name in main thread after it has been completed.
The Future.get() is used for blocking the main thread execution until the corresponding task submitted to the executor is not completed. It can, optionally, take a timeout period after which it will unblock with a TimeOutException.
import java.time.LocalDateTime;
import java.util.concurrent.*;
public class CallableFutureExample {
public static void main(String[] args) throws ExecutionException,
InterruptedException, TimeoutException {
ExecutorService executor = Executors.newFixedThreadPool(1);
Future<String> future = executor.submit(new Work("Demo-Task"));
String completedThread = future.get(10, TimeUnit.SECONDS);
System.out.println("Thread completed : " + completedThread);
}
}
class Work implements Callable<String> {
private final String name;
public Work(String name) {this.name = name;}
@Override
public String call() {
System.out.println("Task [" + name + "] executed on : " + LocalDateTime.now().toString());
return name;
}
}
Program output.
Task [Demo-Task] executed on : 2022-08-07T15:57:33.372000500
Thread completed : Demo-Task
Here we sent a Callable
object to be executed in an executor using the submit()
method. This method receives a Callable
object as a parameter and returns a Future
object that we can use with two main objectives –
- We can control the status of the task – we can cancel the task and check if it has finished. For this purpose, we can use the
isDone()
method to check if the tasks had finished. - We can get the result returned by the call() method. For this purpose, we have used the
get()
method. This method waits until theCallable
object has finished the execution of thecall()
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 thecall()
method throws an exception, this method throws anExecutionException
exception.
3. Conclusion
In this tutorial, we learned the basics of the Callable and Future interfaces and how to execute callable tasks with ExecutorService. We learned to control the execution using the isDone(), cancel() and get() methods.
Happy Learning !!