Como usar Executors para gerenciar threads em Java?

Executors em Java facilitam o gerenciamento de múltiplas threads, otimizando a concorrência e evitando problemas de desempenho.

Como usar Executors para gerenciar threads em Java?

Em aplicações Java concorrentes, criar e gerenciar threads manualmente pode ser ineficiente e propenso a erros. Para resolver isso, o Java fornece a API Executors, que simplifica a criação, execução e gerenciamento de múltiplas threads.

1. O que é ExecutorService?

O ExecutorService é uma interface que gerencia a execução de tarefas assíncronas, evitando a necessidade de criar threads manualmente.

2. Criando um ExecutorService com Thread Pool

Podemos criar um pool de threads usando Executors.newFixedThreadPool():

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ExemploExecutor {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);

        for (int i = 0; i < 5; i++) {
            executor.execute(() -> {
                System.out.println("Thread executando: " + Thread.currentThread().getName());
            });
        }

        executor.shutdown();
    }
}

Explicação:

  • Criamos um ExecutorService com 3 threads.
  • Submetemos 5 tarefas para execução concorrente.
  • Chamamos shutdown() para liberar os recursos após a execução.

3. Tipos de ExecutorService

Tipo de Executor Descrição
newFixedThreadPool(n) Cria um pool fixo de n threads.
newCachedThreadPool() Cria threads sob demanda, reutilizando as inativas.
newSingleThreadExecutor() Garante que apenas uma thread execute de cada vez.
newScheduledThreadPool(n) Agenda tarefas para execução periódica.

4. Submetendo Tarefas com submit()

Diferente de execute(), o método submit() retorna um Future, permitindo obter o resultado da execução:

import java.util.concurrent.*;

public class ExemploSubmit {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        Future<Integer> resultado = executor.submit(() -> {
            Thread.sleep(1000);
            return 42;
        });

        System.out.println("Resultado: " + resultado.get());
        executor.shutdown();
    }
}

Explicação:

  • submit() permite capturar um valor de retorno usando Future.get().
  • get() bloqueia até que a tarefa termine.

5. Parando um Executor

  • shutdown(): Finaliza o executor após a conclusão das tarefas em andamento.
  • shutdownNow(): Interrompe as tarefas imediatamente.
executor.shutdown(); // Aguarda a execução das tarefas pendentes
executor.shutdownNow(); // Tenta interromper imediatamente

Conclusão

O uso de Executors melhora a eficiência no gerenciamento de múltiplas threads, reduzindo a sobrecarga de criação e destruição de threads manualmente. Dependendo do caso, podemos utilizar pools fixos, dinâmicos ou agendados para otimizar o desempenho da aplicação.

A criação manual de threads em Java pode levar a desperdício de recursos e problemas de gerenciamento. Com a API Executors, é possível controlar pools de threads e distribuir tarefas de maneira eficiente. O uso correto do ExecutorService permite escalabilidade e melhor controle sobre a concorrência, especialmente em aplicações de alto desempenho como servidores web e sistemas distribuídos.

Algumas aplicações:

  • Gerenciamento eficiente de múltiplas tarefas assíncronas
  • Criação de servidores que processam requisições simultâneas
  • Execução de operações em segundo plano sem bloquear a aplicação
  • Redução do overhead na criação de threads

Dicas para quem está começando

  • Use newFixedThreadPool(n) para controle de threads em aplicações previsíveis
  • Evite newCachedThreadPool() se houver risco de sobrecarga
  • Sempre chame shutdown() após a execução para liberar recursos
  • Use submit() quando precisar capturar o retorno da tarefa

Contribuições de Rodrigo Farias

Compartilhe este tutorial: Como usar Executors para gerenciar threads em Java

Compartilhe este tutorial

Continue aprendendo:

Como funciona o conceito de concorrência em Java

A concorrência em Java permite a execução simultânea de tarefas, otimizando o uso do processador e melhorando o desempenho da aplicação.

Tutorial anterior

O que é Callable e Future em Java

Callable e Future permitem executar tarefas assíncronas em Java e capturar os resultados de execução de threads.

Próximo tutorial