Criando e Gerenciando Threads em Java

A abordagem mais direta para criar uma thread em Java é estender a classe Thread e sobrescrever o método run(). A instância criada deve ser iniciada com start().

class MinhaThread extends Thread {
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("ID da thread: " + Thread.currentThread().getId());
    }
}

MinhaThread t = new MinhaThread();
t.start();

Implementando a Interface Runnable

Em vez de herdar de Thread, você pode implementar Runnable e passar a instância para o construtor de Thread. Essa abordagem é preferível quando sua classe já herda de outra classe, pois Java não suporta herança múltipla.

class TarefaRunnable implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("ID da thread: " + Thread.currentThread().getId());
    }
}

Thread t = new Thread(new TarefaRunnable());
t.start();

Utilizando Callable com FutureTask

A interface Callable é uma alternativa a Runnable que permite retornar um valor e lançar exceções verificadas. O método call() substitui run(). Para executar uma Callable, é necessário envolvê-la em um FutureTask.

import java.util.concurrent.*;

class TarefaCallable implements Callable<String> {
    @Override
    public String call() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        System.out.println("ID da thread: " + Thread.currentThread().getId());
        return "Resultado da execução";
    }
}

FutureTask<String> tarefa = new FutureTask<>(new TarefaCallable());
new Thread(tarefa).start();

// get() bloqueia até o resultado estar disponível ou o timeout expirar
String resultado = tarefa.get(3, TimeUnit.SECONDS);
System.out.println(resultado);

Gerenciamento de Threads com ThreadPoolExecutor

O Java fornece a classe utilitária Executors para criar pools de threads, porém as diretrizes de codificação da Alibaba proíbem seu uso. O motivo é que LinkedBlockingQueue, sem um limite definido, pode crescer até o valor máximo de Integer, resultando em OutOfMemoryError. Recomenda-se utilizar ThreadPoolExecutor diretamente, definindo explicitamente o tamanho da fila de bloqueio.

Parâmetros do Construtor do ThreadPoolExecutor

  • corePoolSize: número mínimo de threads mantidas no pool, mesmo sem tarefas em execução. Threads adicionais além desse limite só são criados quando a fila de trabalho está cheia.
  • maximumPoolSize: número máximo de threads que o pool pode conter. Pode ser alterado após a criação com setMaximumPoolSize().
  • keepAliveTime: tempo máximo que uma thread ociosa pode aguardar antes de ser finalizada, quando o pool possui mais threads que o corePoolSize.
  • unit: unidade de tempo do parâmetro keepAliveTime.
  • workQueue: fila que armazena tarefas aguardando execução. É obrigatório definir um tamanho para evitar OOM.
  • threadFactory: fábrica responsável por criar novas threads.
  • RejectedExecutionHandler: estratégia aplicada quando uma tarefa é rejeitada pelo pool.

Fluxo de Uso

  1. Crie uma classe que implemente Runnable ou Callable.
  2. Instancie o ThreadPoolExecutor com os parâmetros apropriados.
  3. Envie tarefas ao pool usando submit(), que aceita Runnable ou Callable. Para Runnable, é possível passar um resultado opcional como segundo argumento.
  4. Recupere o resultado da execução através de Future.get(). Essa chamada é bloqueante.
  5. Encerre o pool com shutdown() ou shutdownNow(). O primeiro bloqueia novas submissões, aguarda a conclusão das tarefas em execução e descarta as não iniciadas. O segundo tenta interromper todas as threads em execução, podendo lançar exceções.
ThreadPoolExecutor pool = new ThreadPoolExecutor(
    3,
    10,
    60L,
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(10)
);

Future<?> futuro = pool.submit(new TarefaCallable());
System.out.println(futuro.get());

pool.shutdown();

Tags: java threads ThreadPoolExecutor Callable Runnable

Publicado em 7-25 16:51