Programação Assíncrona
Foto de Luis P.
Por: Luis P.
15 de Janeiro de 2025

Programação Assíncrona

CompletableFuture no Java

Java CompletableFuture

A programação assíncrona é uma abordagem poderosa para melhorar a performance de aplicações que executam tarefas demoradas ou dependem de operações externas, como chamadas a APIs ou acesso a bancos de dados. No Java, a classe CompletableFuture, introduzida no Java 8, oferece um modelo funcional para trabalhar com operações assíncronas e pipelines de tarefas.

Neste artigo, exploraremos como usar o CompletableFuture para criar e gerenciar fluxos de execução assíncronos, além de destacar boas práticas para evitar problemas comuns.


O Que é o CompletableFuture?

O CompletableFuture é uma implementação da interface Future que permite:

  1. Executar tarefas de forma assíncrona.
  2. Encadear múltiplas tarefas em pipelines.
  3. Combinar os resultados de múltiplas operações.
  4. Manipular exceções de maneira funcional.

Criando um CompletableFuture

1. Operações Assíncronas Básicas

Você pode iniciar uma tarefa assíncrona usando o método runAsync ou supplyAsync:

Exemplo com runAsync:

import java.util.concurrent.CompletableFuture;

public class ExemploRunAsync {
    public static void main(String[] args) {
        CompletableFuture.runAsync(() -> {
            System.out.println("Executando tarefa assíncrona...");
        });
    }
}

Exemplo com supplyAsync:

import java.util.concurrent.CompletableFuture;

public class ExemploSupplyAsync {
    public static void main(String[] args) {
        CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
            return "Resultado da tarefa assíncrona";
        });

        future.thenAccept(System.out::println);
    }
}

Encadeamento de Tarefas

1. Usando thenApply

Transforme o resultado de uma tarefa para criar uma nova tarefa:

Exemplo:

CompletableFuture.supplyAsync(() -> "Java")
    .thenApply(s -> s + " é incrível!")
    .thenAccept(System.out::println);

2. Usando thenCompose

Encadeia duas tarefas dependentes, criando um pipeline de execução.

Exemplo:

CompletableFuture.supplyAsync(() -> "Tarefa 1")
    .thenCompose(resultado -> CompletableFuture.supplyAsync(() -> resultado + " + Tarefa 2"))
    .thenAccept(System.out::println);

3. Usando thenCombine

Combina os resultados de duas tarefas independentes.

Exemplo:

CompletableFuture<Integer> tarefa1 = CompletableFuture.supplyAsync(() -> 10);
CompletableFuture<Integer> tarefa2 = CompletableFuture.supplyAsync(() -> 20);

CompletableFuture<Integer> resultado = tarefa1.thenCombine(tarefa2, Integer::sum);

resultado.thenAccept(System.out::println); // Saída: 30

Tratamento de Exceções

O CompletableFuture permite manipular exceções de forma declarativa com métodos como exceptionally e handle.

1. Usando exceptionally

Retorna um valor padrão quando ocorre uma exceção:

Exemplo:

CompletableFuture.supplyAsync(() -> {
    throw new RuntimeException("Erro na tarefa!");
})
.exceptionally(ex -> "Valor padrão")
.thenAccept(System.out::println);

2. Usando handle

Permite tratar o resultado da tarefa e a exceção no mesmo método.

Exemplo:

CompletableFuture.supplyAsync(() -> {
    throw new RuntimeException("Erro na tarefa!");
})
.handle((resultado, ex) -> {
    if (ex != null) {
        return "Erro tratado: " + ex.getMessage();
    }
    return resultado;
})
.thenAccept(System.out::println);

Combinando Múltiplas Tarefas

1. Usando allOf

Espera todas as tarefas terminarem:

Exemplo:

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

todas.join();

2. Usando anyOf

Retorna quando qualquer uma das tarefas termina:

Exemplo:

CompletableFuture<Object> qualquer = CompletableFuture.anyOf(
    CompletableFuture.supplyAsync(() -> "Resultado 1"),
    CompletableFuture.supplyAsync(() -> "Resultado 2")
);

qualquer.thenAccept(System.out::println);

Boas Práticas ao Usar CompletableFuture

  1. Evite Bloqueios Explícitos: Prefira thenApply, thenAccept ou join em vez de usar get para evitar bloqueios desnecessários.
  2. Use Pools de Threads Adequados: Especifique um Executor personalizado para evitar sobrecarga no pool padrão do ForkJoinPool.
  3. Trate Exceções Explicitamente: Sempre forneça mecanismos de fallback ou tratamento de erros.
  4. Evite Pipelines Muito Longos: Divida tarefas complexas em subcomponentes para facilitar a leitura e manutenção.

O CompletableFuture é uma ferramenta poderosa para lidar com operações assíncronas e fluxos complexos de tarefas no Java. Com ele, você pode criar pipelines de execução flexíveis, combinar resultados de tarefas independentes e lidar com erros de forma eficiente. Adotar boas práticas e explorar seus recursos avançados pode melhorar significativamente a performance e a legibilidade do seu código. Experimente o CompletableFuture em seus projetos e aproveite suas vantagens no desenvolvimento moderno!

Luis P.
Luis P.
Boa Ventura / PB
Responde em 13 h e 46 min
Identidade verificada
1ª hora grátis
5,0
nota média
1
avaliação
R$ 40
por hora
Graduação: Licenciatura em Computação (UEPB - Universidade Estadual da Paraíba)
Java - Springboot, Java para Web, Testes em Java
Professor de informática, português e inglês. Formado em computação e letras inglês/português. Servidor público aprovado em 3 concursos.

Confira artigos similares

Aprenda sobre qualquer assunto