Concorrência e Multithreading em Java: Técnicas Avançadas

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Concorrência e Multithreading em Java: Nível Avançado

\n\n

Multithreading em Java é um dos tópicos mais poderosos e, ao mesmo tempo, complexos. No nível básico, usamos synchronized e Thread, mas para construir sistemas performáticos e escaláveis, é necessário um entendimento mais profundo. Neste artigo, abordaremos mecanismos avançados de concorrência: pools de threads, ForkJoinPool, programação assíncrona com CompletableFuture, ajuste fino de locks e gerenciamento de memória. O material é voltado para desenvolvedores que já conhecem os fundamentos do multithreading.

\n\n

1. Trabalho Avançado com Pools de Threads: ExecutorService e Configuração

\n\n

O uso de ExecutorService é o padrão no desenvolvimento profissional. No entanto, o Executors.newFixedThreadPool(10) padrão nem sempre é adequado. Vamos explorar a configuração fina do ThreadPoolExecutor.

\n\n
import java.util.concurrent.*;\n\npublic class CustomThreadPool {\n    public static void main(String[] args) {\n        int corePoolSize = 4;\n        int maxPoolSize = 8;\n        long keepAliveTime = 30;\n        TimeUnit unit = TimeUnit.SECONDS;\n        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);\n\n        ThreadPoolExecutor executor = new ThreadPoolExecutor(\n                corePoolSize,\n                maxPoolSize,\n                keepAliveTime,\n                unit,\n                workQueue,\n                new ThreadPoolExecutor.CallerRunsPolicy() // política de saturação\n        );\n\n        // Monitoramento do estado do pool\n        executor.submit(() -> {\n            System.out.println("Threads ativas: " + executor.getActiveCount());\n            System.out.println("Tarefas concluídas: " + executor.getCompletedTaskCount());\n        });\n\n        executor.shutdown();\n    }\n}
\n\n

Parâmetros-chave:

\n
    \n
  • corePoolSize — número mínimo de threads mantidas mesmo sem tarefas.
  • \n
  • maxPoolSize — número máximo de threads.
  • \n
  • keepAliveTime — tempo de vida de threads excedentes.
  • \n
  • workQueue — fila de tarefas. ArrayBlockingQueue com tamanho limitado evita estouro de memória.
  • \n
  • Política de saturação — o que fazer quando a fila está cheia e todas as threads estão ocupadas. CallerRunsPolicy desacelera a thread chamadora.
  • \n
\n\n

Para tarefas longas de I/O, é melhor aumentar o corePoolSize; para tarefas CPU-bound, baseie-se em Runtime.getRuntime().availableProcessors().

\n\n

2. ForkJoinPool e Tarefas Recursivas

\n\n

ForkJoinPool é um pool especializado para tarefas que podem ser divididas em subtarefas (dividir para conquistar). Ele utiliza eficientemente o roubo de trabalho (work stealing).

\n\n

Exemplo: cálculo da soma dos elementos de um array recursivamente.

\n\n
import java.util.concurrent.RecursiveTask;\nimport java.util.concurrent.ForkJoinPool;\n\npublic class SumTask extends RecursiveTask<Long> {\n    private static final int THRESHOLD = 1000;\n    private final int[] array;\n    private final int start;\n    private final int end;\n\n    public SumTask(int[] array, int start, int end) {\n        this.array = array;\n        this.start = start;\n        this.end = end;\n    }\n\n    @Override\n    protected Long compute() {\n        if (end - start <= THRESHOLD) {\n            long sum = 0;\n            for (int i = start; i < end; i++) {\n                sum += array[i];\n            }\n            return sum;\n        }\n        int mid = (start + end) / 2;\n        SumTask leftTask = new SumTask(array, start, mid);\n        SumTask rightTask = new SumTask(array, mid, end);\n\n        leftTask.fork(); // execução assíncrona\n        long rightResult = rightTask.compute(); // calcula                

Blogs

Book Recommendations