Callable and Future in Java

Naveen AnamaneniUpdated · 4 min read

Callable<V> is a task that returns a result of type V and may throw a checked exception. When you submit a Callable to an executor, you immediately get back a Future<V>: a placeholder for a result that isn't ready yet. Later you call future.get(), which waits if necessary and then returns the result, or throws an ExecutionException if the task failed. In this lesson, you will learn how to use Callable and Future, including timeouts and cancellation.

Declaration

Declaration
@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

Methods of Future

MethodDescription
V get()It is used to wait for the task and return its result.
V get(long timeout, TimeUnit unit)It waits at most the given time, then throws TimeoutException.
boolean isDone()It returns true once the task has finished in any way.
boolean cancel(boolean mayInterrupt)It tries to cancel the task.
boolean isCancelled()It returns true if the task was canceled.
State state() / resultNow()It returns the state, or the result of a completed task (Java 19).

Example: Computing Results in Parallel

Let's see an example that totals the marks of three students in parallel and collects the results.

CallableDemo.java▶ Try it
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
 
public class CallableDemo {
    public static void main(String[] args) throws Exception {
        Map<String, int[]> marks = Map.of(
            "Navika", new int[] {91, 88, 95},
            "Srihitha", new int[] {78, 82, 80},
            "Advitha", new int[] {85, 90, 87});
 
        try (ExecutorService pool = Executors.newFixedThreadPool(3)) {
            List<String> names = List.of("Navika", "Srihitha", "Advitha");
            List<Future<Integer>> futures = new ArrayList<>();
            for (String name : names) {
                Callable<Integer> total = () -> {
                    int sum = 0;
                    for (int m : marks.get(name)) sum += m;
                    return sum;
                };
                futures.add(pool.submit(total));
            }
            for (int i = 0; i < names.size(); i++) {
                System.out.println(names.get(i) + ": " + futures.get(i).get());
            }
        }
    }
}
Output
Navika: 274
Srihitha: 240
Advitha: 262

Explanation: Each Callable returns an Integer. submit returns a Future<Integer> at once, while the totals are computed on pool threads. get() waits for each result in turn. Because the futures are kept in the same order as the names, the output is in a fixed order no matter which task finished first.

Example: Failures, Timeouts and Cancellation

FutureProblems.java▶ Try it
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
 
public class FutureProblems {
    public static void main(String[] args) throws InterruptedException {
        try (ExecutorService pool = Executors.newFixedThreadPool(2)) {
            Future<Integer> failing = pool.submit(() -> Integer.parseInt("9l"));
            try {
                failing.get();
            } catch (ExecutionException e) {
                System.out.println("Task failed: " + e.getCause());
            }
 
            Future<String> slow = pool.submit(() -> {
                Thread.sleep(5000);
                return "late result";
            });
            try {
                slow.get(200, TimeUnit.MILLISECONDS);
            } catch (TimeoutException e) {
                System.out.println("Gave up waiting after 200 ms");
            } catch (ExecutionException e) {
                System.out.println("Unexpected: " + e.getCause());
            }
            System.out.println("Canceled: " + slow.cancel(true)
                + ", state: " + slow.state());
        }
    }
}
Output
Task failed: java.lang.NumberFormatException: For input string: "9l"
Gave up waiting after 200 ms
Canceled: true, state: CANCELLED

Explanation: The first task throws NumberFormatException; get() wraps it in an ExecutionException, and getCause() reveals the original. The second task is too slow, so get with a timeout throws TimeoutException. cancel(true) interrupts the sleeping task, and its state becomes CANCELLED (the enum constant uses the British spelling).

Common Mistakes

Calling get() right after each submit. The tasks then run one at a time; submit all first, then call get.

Calling get() without a timeout on a task that might hang. Your thread may wait forever.

Ignoring the Future. Errors in the task are silently lost.

Interview Corner

What are Callable and Future?

Callable<V> is a functional interface whose call() method returns a value and may throw a checked exception. Submitting it to an ExecutorService returns a Future<V>, which represents the pending result: get() blocks until the task completes and returns the value, throws ExecutionException wrapping any exception from the task, or TimeoutException with a timed get; cancel attempts to stop the task, and isDone/isCancelled report its status.

Practice

  1. Easy: Submit a Callable that returns the length of "Anamaneni" and print the result from the Future.
  2. Harder: Submit five Callable tasks with random sleeps, then use invokeAny to print the result of whichever finishes first.

Quick check: Callable and Future

4 questions
1What can Callable do that Runnable can't?
2What does future.get() do?
3How is an exception thrown inside the task reported?
4What does get(200, MILLISECONDS) throw if the task isn't done?

Key Takeaways

Was this tutorial helpful?

Lesson 414 of 482