Encerramento de Threads em Java
Em Java, encerrar threads de forma segura é essencial para evitar problemas de concorrência e inconsistência de dados. O método stop() da classe Thread foi descontinuado por causar liberação de locks e interromper código crítico de maneira perigosa. Abaixo, exploramos alternativas viáveis.
- Uso de Flag de Saída
Uma abordagem comum é utilizar uma variável de controle (flag) para sinalizar a thread quando ela deve finalizar. A thread verifica essa flag em cada iteração de seu laço principal.
public class WorkerThread extends Thread {
private volatile boolean running = true;
public void stopThread() {
running = false;
}
public void run() {
while (running) {
// Executa tarefas aqui
}
System.out.println("Thread finalizada.");
}
}
// Uso no código principal
WorkerThread worker = new WorkerThread();
worker.start();
Thread.sleep(5000); // Espera 5 segundos
worker.stopThread();
worker.join();
A palavra-chave volatile garante que alterações na flag sejam visíveis imediatamente em todas as threads.
- Método
interrupt()
O método interrupt() é a forma recomendada para interromper threads, especialmente quando estão em estados de bloqueio como sleep() ou wait().
2.1 Threads em Estado de Bloqueio
Se a thread está dormindo ou esperando, chamar interrupt() lança uma InterruptedException. Capturar essa exceção permite encerrar a thread de forma controlada.
public class InterruptibleTask extends Thread {
public void run() {
try {
Thread.sleep(60000); // Dorme por 60 segundos
} catch (InterruptedException e) {
System.out.println("Thread interrompida durante o sleep.");
}
System.out.println("Execução concluída.");
}
}
// No código principal
InterruptibleTask task = new InterruptibleTask();
task.start();
task.interrupt(); // Interrompe a thread
task.join();
2.2 Threads em Execução Ativa
Para threads em laços contínuos, combine interrupt() com a verificação de isInterrupted().
public class ActiveWorker extends Thread {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
// Processamento contínuo
}
System.out.println("Thread encerrada por interrupção.");
}
}
- Considerações para I/O Bloqueante
Operações de I/O bloqueante (ex.: leitura de sockets) podem não responder a interrupt() em todas as plataformas. Uma solução é usar canais interrompíveis (InterruptibleChannel).
import java.io.*;
import java.nio.channels.*;
public class InterruptibleIO {
public static void main(String[] args) throws Exception {
FileInputStream fis = new FileInputStream(FileDescriptor.in);
InterruptibleChannel channel = fis.getChannel();
BufferedReader reader = new BufferedReader(
new InputStreamReader(Channels.newInputStream(channel))
);
Thread readerThread = new Thread(() -> {
try {
String line = reader.readLine();
System.out.println("Linha: " + line);
} catch (ClosedByInterruptException e) {
System.out.println("I/O interrompido pelo canal.");
} catch (IOException e) {
e.printStackTrace();
}
});
readerThread.start();
Thread.sleep(2000);
readerThread.interrupt();
readerThread.join();
}
}
- Padrão de Encerramento com Dupla Verificação
Para garantir que a thread realmente pare, combine uma flag volátil com interrupt().
public class ReliableThread extends Thread {
private volatile boolean terminated = false;
public void terminate() {
terminated = true;
interrupt(); // Acorda threads bloqueadas
}
public void run() {
while (!terminated && !isInterrupted()) {
try {
Thread.sleep(100);
// Trabalho aqui
} catch (InterruptedException e) {
System.out.println("Interrompido.");
break; // Sai do laço
}
}
System.out.println("Thread finalizada de forma segura.");
}
}
Conclusão Técnica
Evite métodos como stop() ou suspend(). Utilize flags voláteis e interrupt() adaptados ao contexto da thread. Teste cuidadosamente para evitar estados inconsistentes.