No universo da programação concorrente em Java, o conceito de "interrupção de thread" difere significativamente de uma interrupção de hardware ou uma exceção de sistema operacional. Em Java, a interrupção é um mecanismo cooperativo para sinalizar a uma thread que ela deve cessar suas operações atuais e encerrar de forma controlada. Ao contrário de métodos obsoletos e perigosos como Thread.stop(), que pode levar a estados inconsistentes do sistema e vazamento de recursos, a interrupção oferece uma abordagem mais elegante e segura.
É fundamental que as threads respondam a essas solicitações de interrupção de forma voluntária. A plataforma Java não oferece um meio para forçar o encerramento imediato de uma thread. Em vez disso, ela fornece um sinal — o status de interrupção da thread — que a thread pode monitorar e agir de acordo. A melhor prática é sempre utilizar o mecanismo de interrupção nativo da thread, em vez de implementar flags de controle personalizados, pois o status de interrupção nativo é automaticamente verificado e reage em cenários de bloqueio (como sleep, wait, join, ou operações de I/O bloqueantes).
Cenários Comuns para Interrupção de Threads
A interrupção de threads é aplicável em duas categorias principais de situações:
- Interrupção de Threads Bloqueadas: Quando uma thread está aguardando em métodos como
Thread.sleep(),Object.wait(),Thread.join(), ou em certas operações de I/O bloqueantes. Uma interrupção nessas situações normalmente resulta em umaInterruptedException, que quebra o estado de bloqueio e permite que a thread lide com o sinal. - Interrupção de Threads em Execução Contínua: Para threads que executam lógica em um loop contínuo sem entrar em um estado de bloqueio. Nesses casos, a própria thread deve verificar periodicamente seu status de interrupção e reagir adequadamente, geralmente saindo do loop e liberando recursos.
Métodos Essenciais para Gerenciar Interrpuções
Existem três métodos primordiais para trabalhar com o status de interrupção de uma thread:
<pre>void interrupt()</pre>: Este é um método de instância (não estático) da classeThread. Ele define o flag de interrupção da thread comotrue. Quando chamado em uma thread que está atualmente bloqueada (emsleep,wait,join, etc.), ele lança umaInterruptedExceptionna thread bloqueada e, notavelmente, limpa o flag de interrupção (definindo-o de volta parafalse). Para threads em execução normal, o flag é simplesmente definido comotrue, e a thread continua sua execução até que ela mesma verifique o flag.<pre>boolean isInterrupted()</pre>: Também um método de instância. Ele verifica o status do flag de interrupção da thread na qual é invocado. Este método não altera o estado do flag de interrupção; ele apenas o consulta. Retornatruese a thread foi interrompida efalsecaso contrário.<pre>static boolean interrupted()</pre>: Este é um método estático da classeThread. Ele verifica o status do flag de interrupção da thread atualmente em execução (a thread que chama o método). A característica mais importante deste método é que ele limpa o flag de interrupção (define-o de volta parafalse) após a verificação. Por essa razão, se chamado duas vezes consecutivas sem outra interrupção, a segunda chamada retornaráfalse.
Exemplos Práticos de Interrupção de Threads
1. Thread Ignorando o Sinal de Interrupção
Neste exemplo, uma thread é iniciada, e a thread principal tenta interrompê-la. No entanto, como a thread filha não verifica seu próprio status de interrupção, ela continua sua execução indefinidamente.
import java.util.concurrent.TimeUnit;
public class ExemploInterrupcao {
public static void main(String[] args) throws InterruptedException {
Thread tarefaInfinita = new Thread(() -> {
while (true) {
System.out.println("Tarefa contínua em execução...");
}
});
tarefaInfinita.start();
System.out.println("Status de interrupção inicial: " + tarefaInfinita.isInterrupted()); // false
TimeUnit.SECONDS.sleep(1); // Aguarda um pouco antes de tentar interromper
tarefaInfinita.interrupt(); // Sinaliza a interrupção
TimeUnit.SECONDS.sleep(1); // Dá tempo para o sinal ser processado (se houvesse um handler)
System.out.println("Status de interrupção após sinal: " + tarefaInfinita.isInterrupted()); // true
// A tarefa 'tarefaInfinita' continua a ser executada, pois não verifica o flag.
}
}
2. Thread Respondendo ao Sinal de Interrupção (Não Bloqueada)
Aqui, a thread verifica ativamente seu status de interrupção. Uma vez que o método interrupt() é chamado na thread principal, a thread filha detecta o flag e encerra sua execução de forma limpa.
import java.util.concurrent.TimeUnit;
public class ExemploInterrupcaoNaoBloqueada {
public static void main(String[] args) throws InterruptedException {
Thread tarefaComVerificacao = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Processando dados na subtarefa...");
try {
TimeUnit.MILLISECONDS.sleep(200); // Simula trabalho
} catch (InterruptedException e) {
// Esta exceção não deve ocorrer aqui, pois a thread não está bloqueada pelo sleep neste contexto
// Mas se estivesse, a interrupção redefiniria o flag.
Thread.currentThread().interrupt(); // Re-interrompe para manter o flag true.
}
}
System.out.println("Subtarefa detectou interrupção e está finalizando.");
});
tarefaComVerificacao.start();
System.out.println("Status de interrupção inicial da subtarefa: " + tarefaComVerificacao.isInterrupted()); // false
TimeUnit.SECONDS.sleep(2); // Deixa a subtarefa executar por um tempo
tarefaComVerificacao.interrupt(); // Sinaliza para a subtarefa parar
TimeUnit.SECONDS.sleep(1); // Aguarda a subtarefa finalizar
System.out.println("Status de interrupção da subtarefa após finalização: " + tarefaComVerificacao.isInterrupted()); // true (se o loop encerrou, o flag permanece true)
}
}
3. Interrompendo uma Thread em Estado Bloqueado
Este é um cenário comum onde uma thread está bloqueada (por exemplo, por Thread.sleep()). Quando interrupt() é chamado, a thread bloqueada recebe uma InterruptedException, e o flag de interrupção é automaticamente limpo. É crucial redefinir o flag de interrupção dentro do bloco catch se a thread deve ser encerrada ou continuar a processar a interrupção de alguma forma.
import java.util.concurrent.TimeUnit;
public class ExemploInterrupcaoBloqueada {
public static void main(String[] args) throws InterruptedException {
Thread tarefaBloqueada = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Tarefa em execução, prestes a dormir...");
try {
TimeUnit.SECONDS.sleep(5); // A thread entra em estado bloqueado
System.out.println("Tarefa acordou naturalmente."); // Se acordar sem interrupção
} catch (InterruptedException e) {
System.out.println("A tarefa foi interrompida enquanto dormia!");
// Quando InterruptedException é lançada, o flag de interrupção é LIMPO (definido como false)
System.out.println("Status de interrupção no catch: " + Thread.currentThread().isInterrupted()); // false
System.err.println("Realizando limpeza de recursos após interrupção.");
// É vital re-interromper a thread para que o loop 'while' possa detectar a interrupção
// e terminar a execução, ou para que chamadas subsequentes possam ver o flag.
Thread.currentThread().interrupt();
}
System.out.println("Lógica de processamento contínua ou finalização.");
}
System.out.println("Tarefa bloqueada finalizada cooperativamente.");
});
tarefaBloqueada.start();
TimeUnit.SECONDS.sleep(1); // Deixa a thread começar e bloquear
tarefaBloqueada.interrupt(); // Interrompe a thread enquanto ela está dormindo
System.out.println("Status de interrupção da thread principal após chamar interrupt(): " + tarefaBloqueada.isInterrupted()); // true
TimeUnit.SECONDS.sleep(2); // Aguarda para observar a saída
}
}
4. Interrupção Elegante de Threads Não Bloqueadas com Thread.interrupted()
Este exemplo demonstra uma maneira compacta de lidar com interrupções em threads que não estão bloqueadas, utilizando o método estático Thread.interrupted(), que, além de verificar, também limpa o flag de interrupção.
import java.util.concurrent.TimeUnit;
public class ExemploInterrupcaoElegante {
public static void main(String[] args) throws InterruptedException {
Thread tarefaElegante = new Thread(() -> {
while (true) {
// Thread.interrupted() verifica o flag E o redefine para false
if (Thread.interrupted()) {
System.out.println("Sinal de interrupção detectado. Liberando recursos...");
break; // Sai do loop
}
System.out.println("Executando operação rotineira...");
try {
TimeUnit.MILLISECONDS.sleep(300); // Simula trabalho
} catch (InterruptedException e) {
System.out.println("Interrompido durante o sono! Re-interrompendo e saindo.");
Thread.currentThread().interrupt(); // Re-interrompe para que o loop na próxima iteração saia.
break;
}
}
System.out.println("Tarefa elegante finalizada.");
});
tarefaElegante.start();
TimeUnit.SECONDS.sleep(2); // Permite que a tarefa execute por um tempo
tarefaElegante.interrupt(); // Sinaliza a interrupção
TimeUnit.SECONDS.sleep(1); // Aguarda a finalização
}
}