CompletableFuture em Java: Guia completo para programação assíncrona

Os CompletableFuture são uma evolução dos tradicionais Future no Java, oferecendo uma abordagem robusta para a programação assíncrona. Neste post, vamos explorar os benefícios, casos de uso e exemplos práticos que ilustram como essa ferramenta pode ser poderosa no desenvolvimento de aplicações Java.
Benefícios do CompletableFuture
-
Não bloqueio: Diferente de outras formas de Future, CompletableFuture permite a execução de callbacks sem bloquear threads, o que melhora a eficiência do uso dos recursos do sistema.
-
Encadeamento de tarefas: Você pode encadear várias operações de forma assíncrona, o que facilita a manipulação de tarefas dependentes.
-
Tratamento de exceções: Oferece mecanismos integrados para lidar com erros de uma maneira mais granular e controlada.
-
Flexibilidade: Permite combinar múltiplas tarefas assíncronas de maneiras complexas, promovendo uma maior modularidade no código.
Casos de Uso
-
Serviços Web Assíncronos: Ideal para desenvolvimento de APIs que necessitam realizar várias chamadas externas ou operações I/O sem bloquear o servidor.
-
Aplicações de Microserviços: Facilita o gerenciamento de múltiplas chamadas a serviços distintos que podem ser executadas em paralelo.
-
Processamento de Dados em Grande Escala: Permite a execução e o gerenciamento de múltiplas tarefas de processamento de dados simultaneamente, otimizando o tempo de resposta.
Exemplos
Utilizando runAsync
Este exemplo demonstra como iniciar uma tarefa assíncrona que não retorna um valor. CompletableFuture.runAsync
aceita um Runnable
e executa-o em um executor assíncrono, que por padrão é o ForkJoinPool.commonPool()
.
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Executando tarefa assíncrona sem retorno.");
});
future.get(); // Aguarda a conclusão da tarefa assíncrona
Utilizando supplyAsync
Aqui, usamos supplyAsync
para iniciar uma tarefa que retorna um valor. Este método é ideal para operações que executam alguma lógica e retornam um resultado, sendo executado de maneira assíncrona no mesmo executor padrão.
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
return "Resultado de tarefa assíncrona com retorno";
});
var resultado = future.get(); // Aguarda e recupera o resultado
Combinando Tarefas com allOf
Este exemplo mostra como aguardar a conclusão de múltiplas CompletableFutures
. O método allOf
é utilizado para criar um novo CompletableFuture
que é concluído quando todas as CompletableFutures
fornecidas são concluídas. Quando todas tarefas serem completadas, o método thenRun
é chamado.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Tarefa 1 completa");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Tarefa 2 completa");
CompletableFuture<Void> allFutures = CompletableFuture.allOf(future1, future2)
.thenRun(() -> System.out.println("Todas as tarefas completas"));
allFutures.join(); // Aguarda a conclusão de todas as tarefas
var valueFuture1 = future1.get(); // recupera o resultado do primeiro CompletableFuture
var valueFuture2 = future2.get(); // recupera o resultado do segundo CompletableFuture
Selecionando a Primeira Tarefa Completa com anyOf
O método anyOf
é usado quando você precisa que qualquer uma das tarefas fornecidas complete primeiro. Ele retorna um CompletableFuture
que é completado com o resultado da primeira CompletableFuture
que completar entre as fornecidas.
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> "Tarefa 1 completa");
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> "Tarefa 2 completa");
CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(future1, future2);
anyFuture.thenAccept(System.out::println); // Imprime resultado do primeiro CompletableFuture a ser completado
var anyValue = (String) anyFuture.get(); // recupera o resultado do primeiro CompletableFuture a ser completado
Utilizando delayedExecutor para Execução Delayed
Este exemplo configura um executor com um delay especificado antes de executar uma tarefa. O método delayedExecutor
cria um executor que inicia a tarefa após um período de tempo definido, aqui sendo 1 segundo.
Executor delayedExecutor = CompletableFuture.delayedExecutor(1, TimeUnit.SECONDS);
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
System.out.println("Execução com delay de 1 segundo.");
}, delayedExecutor);
future.get(); // Aguarda a conclusão da tarefa com delay
Conclusão
CompletableFuture é muito útil e versátil para desenvolvimento Java, especialmente útil para aplicações que necessitam de alta performance e eficiência em processamento assíncrono. Ao incorporar CompletableFuture em seus projetos, você pode obter um controle muito maior sobre as operações assíncronas e seus fluxos de trabalho, além de melhorar significativamente a performance das suas aplicações.