← Back to Advanced Java Course | Chapter 2: Multithreading | Lesson 7 of 8

Java Callable & Future

A Callable is a job that gives back a result, and a Future is a ticket you use to collect that result later, like a claim ticket at a bakery. You can pick up the answer once it is ready.
Syntax
java
Callable<ResultType> task = () -> {
    return result;
};
Future<ResultType> future = executor.submit(task);
ResultType result = future.get();

What is Callable?

The Callable interface represents an asynchronous task, much like Runnable, but with two key differences: a Callable can return a computed value from its call() method, and it can throw checked exceptions during execution, which Runnable's run() cannot.

Example: What is Callable?

java
// Import java.util.concurrent.* so it can be used by its short name
import java.util.concurrent.*;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) throws Exception {
		Callable<Integer> task = () -> {
			if (false) throw new Exception("checked exception allowed");
			// Send 5 * 5 back to the caller
			return 5 * 5;
		};
		// Print a line to the console
		System.out.println(task.call());
	}
}

What is Future?

A Future represents the eventual, pending result of an asynchronous task submitted to an executor. You can poll it with isDone() to check completion status, or call get() to block until the result is ready and retrieve it.

Example: What is Future?

java
// Import java.util.concurrent.* so it can be used by its short name
import java.util.concurrent.*;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) throws Exception {
		// Create a thread pool to run tasks
		ExecutorService executor = Executors.newSingleThreadExecutor();
		Future<Integer> future = executor.submit(() -> 100);
		// Print a line to the console
		System.out.println(future.isDone());
		// Print a line to the console
		System.out.println(future.get());
		executor.shutdown();
	}
}

Handling Exceptions with Future

If a Callable task throws an exception during execution, that exception doesn't propagate immediately — it's captured by the framework and wrapped. When you later call Future.get(), it re-throws that failure wrapped in an ExecutionException, with the original exception available via getCause().

Example: Handling Exceptions with Future

java
// Import java.util.concurrent.* so it can be used by its short name
import java.util.concurrent.*;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) throws InterruptedException {
		// Create a thread pool to run tasks
		ExecutorService executor = Executors.newSingleThreadExecutor();
		Future<Integer> future = executor.submit(() -> { throw new RuntimeException("task failed"); });
		// Try code that might throw an exception
		try {
			future.get();
		// Handle ExecutionException if it was thrown
		} catch (ExecutionException e) {
			// Print a line to the console
			System.out.println("Caught: " + e.getCause().getMessage());
		}
		executor.shutdown();
	}
}

Multiple Callables with invokeAll()

You can run multiple Callable tasks in parallel using an ExecutorService's invokeAll() method, which submits every task at once and blocks until all of them complete. It returns a List of Future objects in the same order the tasks were submitted, letting you retrieve each result individually.

Example: Multiple Callables with invokeAll()

java
// Import java.util.* so it can be used by its short name
import java.util.*;
// Import java.util.concurrent.* so it can be used by its short name
import java.util.concurrent.*;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) throws Exception {
		// Create a thread pool to run tasks
		ExecutorService executor = Executors.newFixedThreadPool(2);
		List<Callable<Integer>> tasks = List.of(() -> 1 + 1, () -> 2 + 2, () -> 3 + 3);
		List<Future<Integer>> results = executor.invokeAll(tasks);
		for (Future<Integer> f : results) System.out.println(f.get());
		executor.shutdown();
	}
}

Best Practices with Future

To keep your application from blocking indefinitely on a hung task, always pass a timeout to Future.get(timeout, unit) rather than calling the no-argument overload. It's also good practice to shut down every executor service inside a finally block so it's released even if a task throws.

Example: Best Practices with Future

java
// Import java.util.concurrent.* so it can be used by its short name
import java.util.concurrent.*;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) {
		// Create a thread pool to run tasks
		ExecutorService executor = Executors.newSingleThreadExecutor();
		// Try code that might throw an exception
		try {
			Future<Integer> future = executor.submit(() -> 42);
			// Print a line to the console
			System.out.println(future.get(2, TimeUnit.SECONDS));
		// Handle Exception if it was thrown
		} catch (Exception e) {
			// Print a line to the console
			System.out.println("Timed out or failed");
		} finally {
			executor.shutdown();
		}
	}
}
Related Topics
{# common_mistakes/chapter_summary/browser_support: on Hindi pages the view already swaps in the hi_ translation fields (or blanks these out if untranslated), so this renders correctly for both languages without a lang_code check here. #}
Common Mistakes
  1. Calling future.get() without a timeout, so the program can block forever if the task hangs; use get(timeout, unit).
  2. Catching only the task's exception type, when get() wraps it in an ExecutionException and you must read e.getCause().
  3. Forgetting that get() blocks until the result is ready, and calling it on the main thread before submitting all tasks, which removes the parallelism.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.