CompletableFuture
Computação assíncrona não bloqueante
CompletableFuture, introduzido no Java 8, permite escrever código assíncrono e não bloqueante de forma fluente. Em vez de bloquear a thread aguardando um resultado, você define pipelines de transformação: o que fazer quando o resultado estiver disponível.
Os métodos principais formam um pipeline: supplyAsync executa uma tarefa assíncrona que retorna um valor, thenApply transforma o resultado (como map), thenAccept consome o resultado (como forEach), thenCompose encadeia futures dependentes, e exceptionally trata erros no pipeline. Os métodos allOf e anyOf combinam múltiplos CompletableFutures.
Por padrão, CompletableFuture usa o ForkJoinPool.commonPool(). Em aplicações com Spring Boot, @Async e Reactive Streams (WebFlux) constroem sobre esses conceitos para escalar a milhares de requisições simultâneas.
import java.util.concurrent.*;
// ── Pipeline assíncrono simples ───────────────────
CompletableFuture<String> futuro = CompletableFuture
.supplyAsync(() -> "dados brutos") // executa em thread separada
.thenApply(dados -> dados.toUpperCase()) // transforma
.thenApply(dados -> "Resultado: " + dados) // transforma novamente
.exceptionally(ex -> "Erro: " + ex.getMessage()); // trata erros
System.out.println(futuro.get()); // Resultado: DADOS BRUTOS
// ── Encadeando operações dependentes ─────────────
CompletableFuture<String> pipeline = CompletableFuture
.supplyAsync(() -> buscarUsuario(42)) // retorna User
.thenCompose(user -> buscarPedidos(user)) // User → Future<List>
.thenApply(pedidos -> "Total: " + pedidos.size())
.exceptionally(ex -> "Falha ao carregar dados");
// ── Executar múltiplas em paralelo ────────────────
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(() -> "API 1");
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(() -> "API 2");
CompletableFuture<String> f3 = CompletableFuture.supplyAsync(() -> "API 3");
CompletableFuture.allOf(f1, f2, f3)
.thenRun(() -> System.out.println("Todas concluídas!"))
.get();Nunca use get() sem timeout em produção — pode bloquear para sempre se a operação travar. Use get(5, TimeUnit.SECONDS) ou orTimeout(5, TimeUnit.SECONDS).