Callable and Future

Getting a Result Back

Runnable.run() returns nothing and cannot throw checked exceptions. Callable<V>.call() returns a value and can throw. submit(callable) returns a Future<V>: a handle to a result that will be ready later.

Future methodMeaning
get()block until the result is ready
get(timeout, unit)block at most this long, else TimeoutException
isDone()finished (normally, with exception, or cancelled)?
cancel(true)try to cancel (interrupts if running)
resultNow(), state() (Java 19)non-blocking result / RUNNING, SUCCESS, FAILED, CANCELLED

Fetching Prices in Parallel

Example

Three "slow" price services are called at the same time. Total time is about the slowest call, not the sum. Results are read in a fixed order.

import java.util.*;
import java.util.concurrent.*;

public class Main {
    static int fetchPrice(String shop, int delayMs, int price) throws InterruptedException {
        Thread.sleep(delayMs);           // pretend network call
        return price;
    }

    public static void main(String[] args) throws Exception {
        try (ExecutorService pool = Executors.newFixedThreadPool(3)) {
            Map<String, Future<Integer>> futures = new LinkedHashMap<>();
            futures.put("Amazon", pool.submit(() -> fetchPrice("Amazon", 300, 1299)));
            futures.put("Flipkart", pool.submit(() -> fetchPrice("Flipkart", 200, 1249)));
            futures.put("Croma", pool.submit(() -> fetchPrice("Croma", 100, 1399)));

            long start = System.nanoTime();
            for (var e : futures.entrySet()) {
                System.out.println(e.getKey() + ": Rs " + e.getValue().get());
            }
            long ms = (System.nanoTime() - start) / 1_000_000;
            System.out.println("Parallel (under 600 ms): " + (ms < 600));
        }
    }
}

Output

Amazon: Rs 1299
Flipkart: Rs 1249
Croma: Rs 1399
Parallel (under 600 ms): true

invokeAll and Exceptions

Example

An exception inside a task is stored in the Future and rethrown by get() wrapped in ExecutionException.

import java.util.*;
import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        List<Callable<String>> tasks = List.of(
                () -> "report-A ready",
                () -> { throw new IllegalStateException("DB down for report-B"); },
                () -> "report-C ready");
        try (ExecutorService pool = Executors.newFixedThreadPool(3)) {
            for (Future<String> f : pool.invokeAll(tasks)) {      // results in task order
                try {
                    System.out.println(f.get());
                } catch (ExecutionException e) {
                    System.out.println("Failed: " + e.getCause().getMessage());
                }
            }
        }
    }
}

Output

report-A ready
Failed: DB down for report-B
report-C ready