What are the differences between Runnable and Callable in Java concurrency?

Runnable and Callable are both functional interfaces representing a task to execute asynchronously, but Callable can return a result and throw checked exceptions, while Runnable can do neither.

Key Points: • Runnable declares void run() — no return value, and it cannot throw checked exceptions from run() itself. • Callable<V> declares V call() throws Exception — it returns a typed result and may throw checked exceptions. • Submitting a Callable to an ExecutorService returns a Future<V> you can use to retrieve the result once complete. • Runnable predates generics-heavy concurrency utilities and is still preferred for fire-and-forget tasks with no result. • Both can be submitted via ExecutorService.submit(), but execute() only accepts Runnable.

Example: A background task that just logs a heartbeat is naturally a Runnable, whereas a task that computes a report and needs to hand the finished report back to the caller is naturally a Callable<Report> submitted to get a Future<Report>.

Code Example:

Runnable task = () -> System.out.println("running");

Callable<Integer> callableTask = () -> {
    return 42;
};

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(callableTask);
Integer result = future.get();

Interview Tip: A concise interview answer is:

"Runnable's run() method returns nothing and can't throw checked exceptions, while Callable's call() method returns a typed result and can throw checked exceptions. I use Callable submitted through an ExecutorService whenever I need the outcome of the asynchronous work back via a Future."