Callables and Futures

A Runnable represents a task that runs asynchronously. Think of it like a method that does not take input and does not return a value. A Callable is similar but returns a value. It has a single method:

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

A Future holds the result of an asynchronous task. When you schedule a task, you get a Future object. Later, you can use it to get the result when it is ready.

The Future<V> interface provides several important methods:

Tip: Always check the task state before calling resultNow() or exceptionNow().

Example with FutureTask

FutureTask implements both Runnable and Future, so you can run it in a thread and get its result:

Callable<Integer> task = () -> {
    Thread.sleep(1000);
    return 42;
};

FutureTask<Integer> futureTask = new FutureTask<>(task);
Thread.ofPlatform().start(futureTask); // runs the task in a thread

Integer result = futureTask.get(); // waits for the task to finish
System.out.println("Task result: " + result);

You can also wrap a Runnable in a FutureTask, but it returns null:

Runnable myRunnable = () -> System.out.println("Running a simple task");
FutureTask<Void> futureTask = new FutureTask<>(myRunnable, null);
Thread.ofPlatform().start(futureTask);
futureTask.get(); // waits for completion, returns null

Canceling Tasks

In Java, you can attempt to cancel a task that is running asynchronously (for example, a Callable or FutureTask) using the cancel method:

futureTask.cancel(true);

The behavior of cancel depends on two factors:

Task Has Not Started Yet

If the task has not yet started, cancellation is simple:

Example:

FutureTask<Integer> futureTask = new FutureTask<>(() -> {
    System.out.println("Running task");
    return 42;
});

// Task not started yet
futureTask.cancel(true);
System.out.println(futureTask.isCancelled()); // true
System.out.println(futureTask.isDone());      // true

The task body (System.out.println("Running task")) never executes.

Task Is Already Running

If the task has started, cancellation becomes tricky:

futureTask.cancel(true);

Interrupting a thread does not automatically stop it. In Java:

Example of cooperative cancellation:

Callable<Void> task = () -> {
    while (!Thread.currentThread().isInterrupted()) {
        // Do some work
        System.out.println("Working...");
        Thread.sleep(500); // sleep can throw InterruptedException
    }
    System.out.println("Task was interrupted!");
    return null;
};

FutureTask<Void> futureTask = new FutureTask<>(task);
Thread t = new Thread(futureTask);
t.start();

Thread.sleep(1000);
futureTask.cancel(true); // interrupts the task

Output may look like:

Working...
Working...
Task was interrupted!

Key points:

Why mayInterrupt Exists:

Always remember: Java cannot force-stop a thread safely. Cancellation is cooperative. The task must cooperate by periodically checking its interrupted statu

Summary

Using Callables and Futures makes it easier to split a program into subtasks, run them in parallel, and combine results efficiently.

Reference: Thread and Task API Methods