Mastering CompletableFuture in Spring Boot 3: 50 Real‑World Async Patterns
This article introduces CompletableFuture, explains its core features and ideal use cases, and provides 50 practical Spring Boot 3 examples covering simple async tasks, chaining, exception handling, timeouts, custom thread pools, HTTP requests, and advanced composition techniques.
1. Introduction
What is CompletableFuture? CompletableFuture resides in the JUC package and represents the future result of an asynchronous computation. Unlike the traditional Future, it offers a rich API for building complex async pipelines, supporting operations such as combining multiple futures, handling exceptions, and non‑blocking execution.
Core features of CompletableFuture:
Non‑blocking: Execute tasks without blocking the main thread.
Task chaining: Link multiple asynchronous tasks together.
Exception handling: Provide elegant ways to handle errors.
Future merging: Combine several futures into one.
When to use:
Asynchronous operations: When tasks can run independently without waiting for each other.
I/O operations: Ideal for network calls, file I/O, or database queries that involve latency.
Complex workflows: When multiple dependent or independent tasks must be executed in a specific order.
Why use:
Performance boost: Leverage non‑blocking operations to better utilize system resources and improve responsiveness.
Simplified code: Compared with traditional callbacks, it enables more elegant asynchronous code management.
Graceful exception handling: Built‑in mechanisms handle exceptions that occur during async processing.
Below are 50 common usage scenarios for CompletableFuture.
2. Practical Cases
2.1 Simple asynchronous task
CompletableFuture.runAsync(() -> {
System.out.println("异步线程执行任务");
});2.2 Asynchronous task returning a result
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 42);2.3 Using thenApply for chaining
CompletableFuture<Integer> future = CompletableFuture
.supplyAsync(() -> 10)
.thenApply(result -> result * 2);2.4 Using thenAccept to process the result
CompletableFuture.supplyAsync(() -> 10)
.thenAccept(System.out::println);2.5 thenRun when the result is not needed
CompletableFuture.supplyAsync(() -> 10)
.thenRun(() -> System.out.println("任务执行完成."));2.6 Combining two futures with thenCombine
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<Integer> ret = f1.thenCombine(f2, Integer::sum);
ret.thenAccept(result -> System.out.println("合并后的结果: " + result));2.7 Waiting for all futures with allOf
CompletableFuture<Void> allFutures = CompletableFuture.allOf(
CompletableFuture.supplyAsync(() -> "Task 1"),
CompletableFuture.supplyAsync(() -> "Task 2")
);
allFutures.join();2.8 Using anyOf to wait for the first completed future
CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(
CompletableFuture.supplyAsync(() -> { Thread.sleep(3000); return "Task 1"; }),
CompletableFuture.supplyAsync(() -> { Thread.sleep(1000); return "Task 2"; })
);
anyFuture.thenAccept(result -> System.out.println("第一个完成的: " + result));2.9 Handling exceptions with exceptionally
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.err.println(1 / 0);
return 666;
}).exceptionally(ex -> {
System.out.println("Exception: " + ex.getMessage());
return -1;
});
System.err.println(future.get());2.10 Using handle to process result and exception together
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.err.println(1 / 0);
return 666;
}).handle((result, ex) -> {
return (ex != null) ? 0 : result;
});2.11 thenCompose to chain dependent tasks
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 10)
.thenCompose(result -> CompletableFuture.supplyAsync(() -> result * 2));2.12 Asynchronous HTTP request
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("http://localhost:8080/users/1"))
.build();
CompletableFuture<HttpResponse<String>> future = client.sendAsync(request, HttpResponse.BodyHandlers.ofString());
future.thenAccept(response -> System.out.println(response.body()));2.13 Specifying a custom thread pool
ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 2, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2));
CompletableFuture.runAsync(() -> System.out.printf("%s - 执行任务", Thread.currentThread().getName()), executor);2.14 Chaining multiple futures
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 10)
.thenApply(r -> r + 5)
.thenApply(r -> r * 2);
future.thenAccept(r -> System.out.println("最终结果: " + r));2.15 Manually completing a future
CompletableFuture<Integer> future = new CompletableFuture<>();
future.complete(10);
future.thenAccept(r -> System.out.println("最终结果: " + r));
future.join();2.16 Setting a timeout for a task
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
Thread.sleep(2000);
return 10;
});
future.orTimeout(1, TimeUnit.SECONDS)
.exceptionally(ex -> { System.out.println("任务执行超时..."); return null; })
.join();2.17 Returning a default value on timeout
CompletableFuture.supplyAsync(() -> {
Thread.sleep(2000);
return 10;
}).completeOnTimeout(666, 1, TimeUnit.SECONDS)
.thenAccept(r -> System.out.println("Result: " + r));2.18 Combining futures with Stream API
List<CompletableFuture<Integer>> futures = List.of(
CompletableFuture.supplyAsync(() -> 10),
CompletableFuture.supplyAsync(() -> 20),
CompletableFuture.supplyAsync(() -> 30)
);
CompletableFuture<Void> combined = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
combined.thenRun(() -> {
List<Integer> results = futures.stream()
.map(CompletableFuture::join)
.toList();
System.out.println(results);
}).join();2.19 Delayed completion
CompletableFuture<Integer> future = new CompletableFuture<>();
Executors.newSingleThreadScheduledExecutor().schedule(() -> future.complete(42), 2, TimeUnit.SECONDS);
future.thenAccept(System.out::println).join();2.20 thenAcceptBoth to combine two results
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> 20);
f1.thenAcceptBoth(f2, (a, b) -> System.out.println("合并结果: " + (a + b))).join();2.21 acceptEither to process the first completed task
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> { Thread.sleep(3000); return 10; });
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> { Thread.sleep(2000); return 20; });
f1.acceptEither(f2, r -> System.out.println("结果: " + r)).join();2.22 Cancelling a task
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
Thread.sleep(2000);
return 10;
});
future.cancel(true);2.23 Using whenComplete for final processing
CompletableFuture.supplyAsync(() -> 10)
.whenComplete((result, ex) -> System.out.println("Final result: " + result))
.join();2.24 Processing a stream of futures
List<CompletableFuture<Integer>> futures = List.of(
CompletableFuture.supplyAsync(() -> { Thread.sleep(2000); return 1; }),
CompletableFuture.supplyAsync(() -> { Thread.sleep(1000); return 2; }),
CompletableFuture.supplyAsync(() -> { Thread.sleep(3000); return 3; })
);
futures.forEach(f -> f.thenAccept(r -> System.out.println("Result: " + r)));2.25 completeExceptionally for forced errors
CompletableFuture<Integer> future = new CompletableFuture<>();
future.completeExceptionally(new RuntimeException("任务执行错误"));
future.exceptionally(ex -> { System.out.println("Exception: " + ex.getMessage()); return 0; });2.26 thenComposeAsync for asynchronous composition
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 5)
.thenComposeAsync(r -> CompletableFuture.supplyAsync(() -> r * 2));
future.thenAccept(r -> System.out.println(r));2.27 thenCombineAsync to combine tasks asynchronously
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<Integer> combined = f1.thenCombineAsync(f2, Integer::sum);
combined.thenAccept(r -> System.out.println(r));2.28 Blocking wait until completion
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 42);
Integer result = future.get();
System.out.println("结果: " + result);2.29 Merging futures of different types
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "Result");
CompletableFuture<String> combined = f1.thenCombine(f2, (num, str) -> num + " " + str);
combined.thenAccept(System.out::println);2.30 Combining thenCombine and thenCompose
CompletableFuture<Integer> f1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> f2 = CompletableFuture.supplyAsync(() -> 20);
CompletableFuture<Integer> finalFuture = f1.thenCombine(f2, Integer::sum)
.thenCompose(r -> CompletableFuture.supplyAsync(() -> r * 2));
finalFuture.thenAccept(r -> System.out.println(r));2.31 handleAsync for asynchronous exception handling
CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
System.err.println(1 / 0);
return 888;
}).handleAsync((result, ex) -> {
if (ex != null) {
System.out.println("Handled exception");
return 0;
}
return result;
});2.32 Waiting for multiple futures with timeout
List<CompletableFuture<Integer>> futures = List.of(
CompletableFuture.supplyAsync(() -> 10),
CompletableFuture.supplyAsync(() -> 20)
);
CompletableFuture<Void> combined = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.orTimeout(1, TimeUnit.SECONDS);
combined.thenRun(() -> System.out.println("All done or timed out"));Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
