Como usar CompletableFuture para programação assíncrona?

O CompletableFuture é uma API poderosa do Java para executar tarefas assíncronas de forma eficiente e sem bloqueios.

Como usar CompletableFuture para programação assíncrona?

O CompletableFuture é uma API do Java 8 que facilita a execução de tarefas assíncronas, permitindo processamento paralelo, encadeamento de operações e tratamento de exceções sem bloquear a thread principal.

1. Criando um CompletableFuture básico

import java.util.concurrent.CompletableFuture;

public class Exemplo {
    public static void main(String[] args) {
        CompletableFuture<String> futuro = CompletableFuture.supplyAsync(() -> {
            try { Thread.sleep(2000); } catch (InterruptedException e) {}
            return "Resultado da tarefa assíncrona";
        });

        futuro.thenAccept(resultado -> System.out.println("Finalizado: " + resultado));
        System.out.println("Processo assíncrono iniciado...");
    }
}

Explicação:

  • supplyAsync() executa a tarefa em uma thread separada.
  • thenAccept() define uma ação que será executada quando a tarefa terminar.
  • O programa continua rodando sem esperar a execução do CompletableFuture.

2. Encadeando tarefas com thenApply()

Podemos processar o resultado antes de exibi-lo:

CompletableFuture.supplyAsync(() -> "Java")
    .thenApply(resultado -> resultado + " Assíncrono")
    .thenAccept(System.out::println);

Saída: Java Assíncrono

3. Combinando múltiplas tarefas

Podemos executar duas operações em paralelo e combinar os resultados:

CompletableFuture<String> futuro1 = CompletableFuture.supplyAsync(() -> "Primeira tarefa");
CompletableFuture<String> futuro2 = CompletableFuture.supplyAsync(() -> "Segunda tarefa");

futuro1.thenCombine(futuro2, (resultado1, resultado2) -> resultado1 + " e " + resultado2)
    .thenAccept(System.out::println);

Saída: Primeira tarefa e Segunda tarefa

4. Tratamento de exceções com exceptionally()

Se uma exceção ocorrer, podemos capturá-la e definir um valor alternativo:

CompletableFuture.supplyAsync(() -> {
    if (Math.random() > 0.5) throw new RuntimeException("Erro inesperado");
    return "Sucesso";
}).exceptionally(ex -> "Erro tratado: " + ex.getMessage())
  .thenAccept(System.out::println);

5. Esperando múltiplas tarefas terminarem

O método allOf() espera que todas as tarefas sejam concluídas antes de prosseguir:

CompletableFuture<Void> todas = CompletableFuture.allOf(
    CompletableFuture.runAsync(() -> System.out.println("Tarefa 1")),
    CompletableFuture.runAsync(() -> System.out.println("Tarefa 2")),
    CompletableFuture.runAsync(() -> System.out.println("Tarefa 3"))
);

todas.join(); // Aguarda todas finalizarem

Conclusão

O CompletableFuture facilita a programação assíncrona, melhorando a eficiência e o desempenho de aplicações que lidam com múltiplas tarefas simultaneamente. Ele permite composição de chamadas assíncronas, tratamento de erros e execução paralela sem bloquear a thread principal.

Em aplicações modernas, a execução assíncrona é essencial para melhorar o desempenho, especialmente em chamadas de API, consultas a banco de dados e processamento de grandes volumes de dados. O CompletableFuture fornece uma maneira eficiente de trabalhar com operações não bloqueantes, permitindo que a aplicação continue responsiva enquanto realiza tarefas complexas.

Algumas aplicações:

  • Processamento paralelo de tarefas demoradas
  • Execução assíncrona de chamadas de API
  • Consulta simultânea a múltiplas fontes de dados
  • Melhoria da responsividade em aplicações web

Dicas para quem está começando

  • Use supplyAsync() para retornar valores assíncronos
  • Encadeie chamadas com thenApply() para transformar resultados
  • Trate exceções com exceptionally() para evitar falhas
  • Use allOf() para esperar múltiplas tarefas terminarem

Contribuições de Rodrigo Farias

Compartilhe este tutorial: Como usar CompletableFuture para programação assíncrona

Compartilhe este tutorial

Continue aprendendo:

Como interromper uma thread corretamente

A interrupção de threads em Java deve ser feita de forma segura para evitar deadlocks e garantir que os recursos sejam liberados corretamente.

Tutorial anterior

Como melhorar o desempenho de uma aplicação Java multithread

Melhorar o desempenho de uma aplicação Java multithread requer técnicas como sincronização eficiente, uso de pools de threads e minimização de bloqueios.

Próximo tutorial