Introdução à programação assíncrona em Java
Aplicações modernas exigem alta performance e capacidade de resposta. Os usuários não estão dispostos a esperar enquanto a aplicação processa uma consulta ao banco de dados ou realiza uma chamada de rede. É aqui que a programação assíncrona em Java entra em cena — uma abordagem que permite não bloquear o fluxo principal de execução enquanto aguarda a conclusão de operações demoradas.
Ao contrário do multithreading tradicional, onde você gerencia explicitamente as threads, a programação assíncrona oferece abstrações de nível mais alto. Em vez de criar novas threads manualmente, você utiliza CompletableFuture, ExecutorService e outras ferramentas fornecidas pela Java Concurrency API.
Neste artigo, abordaremos os conceitos-chave da programação assíncrona em Java com exemplos práticos. Você aprenderá como processar tarefas assíncronas, encadeá-las e trabalhar eficientemente com multithreading.
Ferramentas principais da programação assíncrona em Java
1. CompletableFuture — a base da assincronicidade
CompletableFuture é uma classe introduzida no Java 8 que fornece uma API poderosa para programação assíncrona. Ela permite criar tarefas assíncronas, combiná-las, tratar erros e transformar resultados.
Vejamos um exemplo simples de criação e execução de uma tarefa assíncrona:
import java.util.concurrent.CompletableFuture;import java.util.concurrent.ExecutionException;
public class AsyncExample { public static void main(String[] args) throws ExecutionException, InterruptedException { // Criando uma tarefa assíncrona CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(1000); // Simulando uma operação demorada } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return "Resultado da operação assíncrona"; });
// Adicionando um manipulador de conclusão future.thenAccept(result -> System.out.println("Resultado obtido: " + result));
// O fluxo principal continua trabalhando System.out.println("O fluxo principal continua a execução");
// Aguardando a conclusão (apenas para demonstração) future.get(); }}Observe que supplyAsync() por padrão utiliza ForkJoinPool.commonPool(). Este é um pool de threads otimizado para tarefas paralelas.
2. ExecutorService — gerenciamento do pool de threads
Para um controle mais refinado sobre a execução de tarefas assíncronas, utilize ExecutorService. Isso permite definir o número de threads, seu comportamento e a estratégia de processamento de tarefas.
import java.util.concurrent.*;
public class ExecutorServiceExample { public static void main(String[] args) { // Criando um pool de 4 threads ExecutorService executor = Executors.newFixedThreadPool(4);
// Criando um CompletableFuture com um executor personalizado CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return 42; }, executor);
// Processando o resultado future.thenAccept(result -> { System.out.println("Resultado: " + result); executor.shutdown(); // Importante! Fechar o pool });
System.out.println("O fluxo principal não é bloqueado"); }}Importante: Sempre feche o ExecutorService com shutdown() ou shutdownNow() para evitar vazamento de recursos.