← Back to Advanced Java Course | Chapter 11: Advanced & Security | Lesson 7 of 19

Java CompletableFuture

CompletableFuture lets you start a task in the background and say what should happen when it finishes, like ordering food and being told when it is ready. You can chain steps together.
Syntax
java
CompletableFuture<Type> future = CompletableFuture.supplyAsync(() -> value)
    .thenApply(result -> transformed)
    .thenAccept(result -> action);

What is CompletableFuture?

CompletableFuture, introduced in Java 8, supports asynchronous, non-blocking programming by running tasks on a background thread and letting you attach callbacks that fire when the result is ready, instead of blocking the calling thread to wait for it.

Example: What is CompletableFuture?

java
import java.util.concurrent.CompletableFuture;
public class Main {
	public static void main(String[] args) throws Exception {
		CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "done"); // runs in background
		future.thenAccept(result -> System.out.println("Callback fired: " + result));
		future.get(); // wait so main() doesn't exit before the callback runs
	}
}

Running Tasks with runAsync()

The static runAsync() method executes a task asynchronously on a background thread pool. It accepts a Runnable and returns no result, which makes it the right choice for fire-and-forget side effects like logging or sending a notification.

Example: Running Tasks with runAsync()

java
import java.util.concurrent.CompletableFuture;
public class Main {
	public static void main(String[] args) throws Exception {
		CompletableFuture<Void> future = CompletableFuture.runAsync(() -> System.out.println("Fire-and-forget task")); // no result
		future.get();
	}
}

SupplyAsync and Callbacks

supplyAsync() runs a task that produces a result asynchronously, and chaining its output to thenAccept() lets you process that completed value once it's ready, all without blocking the calling thread at any point in the chain.

Example: SupplyAsync and Callbacks

java
import java.util.concurrent.CompletableFuture;
public class Main {
	public static void main(String[] args) throws Exception {
		CompletableFuture.supplyAsync(() -> 10 * 5)
			.thenAccept(result -> System.out.println("Result: " + result))
			.get(); // never blocks until this final wait
	}
}

Combining Futures

thenCombine() lets you combine two independent CompletableFutures and process both of their results together as soon as they've both finished, which is useful when two unrelated async operations (like two separate API calls) both need to complete before you can proceed.

Example: Combining Futures

java
// Import java.util.concurrent.CompletableFuture so it can be used by its short name
import java.util.concurrent.CompletableFuture;
// Define the class Main
public class Main {
	// Program entry point: the JVM starts running here
	public static void main(String[] args) throws Exception {
		// Start an asynchronous task with CompletableFuture
		CompletableFuture<Integer> a = CompletableFuture.supplyAsync(() -> 5);
		// Start an asynchronous task with CompletableFuture
		CompletableFuture<Integer> b = CompletableFuture.supplyAsync(() -> 10);
		CompletableFuture<Integer> combined = a.thenCombine(b, (x, y) -> x + y);
		// Print a line to the console
		System.out.println(combined.get());
	}
}

Exception Handling

exceptionally() lets you catch and handle errors thrown anywhere inside a chain of async background tasks, letting you supply a safe fallback value instead of the exception propagating uncaught and silently failing the whole async pipeline.

Example: Exception Handling

java
import java.util.concurrent.CompletableFuture;
public class Main {
	public static void main(String[] args) throws Exception {
		CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
			throw new RuntimeException("task failed");
		}).exceptionally(ex -> -1); // safe fallback instead of propagating uncaught
		System.out.println(future.get());
	}
}
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 get() or join() right after supplyAsync, which blocks the thread and removes the benefit of being asynchronous.
  2. Using thenApply with a function that returns another CompletableFuture, giving a nested future; use thenCompose instead.
  3. Ending main before the future finishes, since the default pool uses daemon threads and the program can exit before the result is printed.

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.