A programação assíncrona é um conceito crucial na construção de aplicativos modernos, onde a responsividade é fundamental. O Java 8 introduziu a classe CompletableFuture, que oferece uma maneira elegante e eficiente de realizar operações assíncronas. Neste artigo, exploraremos como utilizar o CompletableFuture
para criar código assíncrono, melhorando o desempenho e a responsividade de sua aplicação. Continuar lendo para aprender mais sobre essa poderosa ferramenta.
O Básico do CompletableFuture
O CompletableFuture
permite que você execute tarefas em segundo plano sem bloquear a thread principal, tornando a execução de operações demoradas mais eficiente. Vamos começar com um exemplo básico:
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class ExemploCompletableFuture {
public static void main(String[] args)
throws ExecutionException, InterruptedException
{
// Criando um CompletableFuture que será completado no futuro
var futuro = CompletableFuture.supplyAsync(() -> {
// Simulando uma operação demorada
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Retornando um resultado
return 42;
});
// Obtendo o resultado quando estiver pronto
int resultado = futuro.get();
System.out.println("Resultado: " + resultado); // Imprime "Resultado: 42"
}
}
Neste exemplo, criamos um CompletableFuture
que executa uma operação demorada em segundo plano e, em seguida, obtemos o resultado quando estiver pronto. Isso permite que a thread principal continue sua execução sem esperar pela conclusão da tarefa assíncrona.
Executando Ações Após a Conclusão
O CompletableFuture
também permite a execução de ações após a conclusão da tarefa. No exemplo a seguir, definimos uma ação que será executada quando o CompletableFuture
estiver completo:
Curso Java - Fundamentos
Conhecer o cursoimport java.util.concurrent.CompletableFuture;
public class ExemploCompletableFuture {
public static void main(String[] args) {
// Criando um CompletableFuture que será completado no futuro
var futuro = CompletableFuture.supplyAsync(() -> {
// Simulando uma operação demorada
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Retornando um resultado
return 42;
});
/*
* Definindo uma ação a ser executada quando
* o CompletableFuture estiver completo
*/
var novoFuturo = futuro.thenAccept(resultado -> {
System.out.println("Resultado: " + resultado); // Imprime "Resultado: 42"
});
// Aguardando a conclusão (isso é opcional, dependendo do contexto)
novoFuturo.join();
}
}
Nesse caso, a ação definida com thenAccept
imprime o resultado quando a operação assíncrona estiver completa.
Combinando Resultados de Múltiplos CompletableFuture
O CompletableFuture
também permite a combinação de resultados de várias tarefas assíncronas. No exemplo a seguir, criamos dois CompletableFutures
com operações assíncronas e combinamos seus resultados:
import java.util.concurrent.CompletableFuture;
public class ExemploCompletableFuture {
public static void main(String[] args) {
// Criando dois CompletableFutures com operações assíncronas
var futuro1 = CompletableFuture.supplyAsync(() -> 10);
var futuro2 = CompletableFuture.supplyAsync(() -> 20);
/*
* Combinando os resultados dos CompletableFutures
* quando ambos estiverem prontos
*/
var resultado = futuro1
.thenCombine(futuro2, (valor1, valor2) -> valor1 + valor2);
// Definindo uma ação a ser executada quando o resultado estiver pronto
resultado.thenAccept(soma -> {
System.out.println("Soma: " + soma); // Imprime "Soma: 30"
});
// Aguardando a conclusão (isso é opcional, dependendo do contexto)
resultado.join();
}
}
Neste exemplo, combinamos os resultados de futuro1
e futuro2
usando thenCombine
, o que nos permite realizar operações com os valores retornados por ambos.
Transformando Resultados
O CompletableFuture
também permite a transformação dos resultados. No exemplo a seguir, aplicamos uma transformação ao resultado de uma tarefa assíncrona:
Curso Java - Orientação a objetos
Conhecer o cursoimport java.util.concurrent.CompletableFuture;
public class ExemploCompletableFuture {
public static void main(String[] args) {
// Criando um CompletableFuture com uma operação assíncrona
var futuro = CompletableFuture.supplyAsync(() -> 10);
// Aplicando uma transformação ao resultado quando estiver pronto
var resultado = futuro.thenApply(valor -> "Resultado: " + valor);
/* Definindo uma ação a ser executada quando
* o resultado transformado estiver pronto
*/
resultado.thenAccept(mensagem -> {
System.out.println(mensagem); // Imprime "Resultado: 10"
});
// Aguardando a conclusão (isso é opcional, dependendo do contexto)
resultado.join();
}
}
Neste exemplo, usamos thenApply
para transformar o resultado de futuro
em uma mensagem formatada.
Tratamento de Erros
O CompletableFuture
também permite o tratamento de erros de maneira elegante. No exemplo a seguir, lidamos com uma exceção lançada por uma tarefa assíncrona:
import java.util.concurrent.CompletableFuture;
public class ExemploCompletableFuture {
public static void main(String[] args) {
// Criando uma CompletableFuture que irá falhar no futuro
var futuro = CompletableFuture.supplyAsync(() -> {
throw new RuntimeException("Erro!");
});
// Tratando o erro
var futuroTratado = futuro.exceptionally(ex -> {
System.out.println("Erro: " + ex.getMessage());
return null;
});
// Aguardando o término da execução
futuroTratado.join(); // Erro: java.lang.RuntimeException: Erro!
}
}
Nesse caso, utilizamos exceptionally
para lidar com a exceção lançada pela tarefa assíncrona, permitindo que a aplicação continue a execução, mesmo em caso de erro.
Melhorando o Desempenho com CompletableFuture
Vamos agora explorar um exemplo prático de como o CompletableFuture
pode ser usado para melhorar o desempenho de uma aplicação. Imagine que você precisa fazer três chamadas de API que demoram um tempo específico para responder. Se fizermos essas chamadas de forma sequencial, o tempo total será a soma dos tempos de cada chamada. No entanto, se fizermos as chamadas de forma concorrente, o tempo total será o tempo da chamada mais demorada.
Curso Java - Collections - Parte 1
Conhecer o cursoimport java.util.concurrent.CompletableFuture;
public class ExemploCompletableFuture {
// Simulando uma chamada de API que demora um tempo específico
public static String fakeApi(long tempo) {
try {
Thread.sleep(tempo);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "OK";
}
public static void sequencial() {
// Chamando a API de forma sequencial
var inicio = System.currentTimeMillis();
fakeApi(1000);
fakeApi(2000);
fakeApi(1500);
var fim = System.currentTimeMillis();
System.out.println("Tempo total: " + (fim - inicio));
}
public static void concorrente() {
// Chamando a API de forma concorrente
var inicio = System.currentTimeMillis();
var futuro1 = CompletableFuture.supplyAsync(() -> fakeApi(1000));
var futuro2 = CompletableFuture.supplyAsync(() -> fakeApi(2000));
var futuro3 = CompletableFuture.supplyAsync(() -> fakeApi(1500));
/*
* Podemos usar o método CompletableFuture.allOf() para aguardar o término de
* todas as chamadas.
*/
CompletableFuture.allOf(futuro1, futuro2, futuro3).join();
var fim = System.currentTimeMillis();
System.out.println("Tempo total: " + (fim - inicio));
}
public static void main(String[] args) {
sequencial(); // Imprime "Tempo total: 4501"
concorrente(); // Imprime "Tempo total: 2005"
}
}
Neste exemplo, comparamos a execução sequencial das chamadas de API com a execução concorrente usando CompletableFuture
. O resultado demonstra como a programação assíncrona pode melhorar significativamente o desempenho da aplicação.
Conclusão
O CompletableFuture
é uma ferramenta poderosa para lidar com programação assíncrona no Java. Com ele, você pode executar tarefas em segundo plano, combinar resultados, transformar dados e lidar com erros de forma eficiente. Além disso, o uso do CompletableFuture
pode melhorar o desempenho de sua aplicação, tornando-a mais responsiva e eficaz. Portanto, considere incorporar o CompletableFuture
em seus projetos para obter benefícios significativos na programação assíncrona.
Curso Java - Stream API
Conhecer o cursoEsperamos que este artigo tenha fornecido uma compreensão sólida do CompletableFuture
e como usá-lo em sua jornada de programação assíncrona no Java. Se você quiser saber mais, não hesite em explorar a documentação oficial do Java e experimentar com exemplos adicionais. O futuro da programação assíncrona no Java está repleto de oportunidades emocionantes!