Atualizado: 07/09/2025

Este conteúdo é original e não foi gerado por inteligência artificial.

Finalização e Interrupção de Threads em Java

Nos exemplos anteriores, uma thread era uma sequência finita de operações. No entanto, muitos cenários exigem threads que operam em ciclos contínuos, como um serviço que verifica a cada minuto por novas tarefas ou um servidor que aguarda indefinidamente por conexões de rede. Nesses casos, é essencial implementar um mecanismo para finalizar a thread de forma controlada.

Finalização Controlada com uma Flag Booleana

Uma abordagem intuitiva para controlar o ciclo de vida de uma thread é usar uma variável booleana como um sinalizador (flag). Enquanto o sinalizador for true, a thread continua seu trabalho. Quando uma outra thread o altera para false, o laço é encerrado, e a thread finaliza sua execução de forma segura.

Vamos definir uma classe que implementa essa lógica:

class MyTask implements Runnable {

    private volatile boolean isActive; // A keyword volatile garante visibilidade entre threads

    void disable() {
        isActive = false;
    }

    MyTask() {
       isActive = true;
    }

    public void run() {
        System.out.printf("%s started... \n", Thread.currentThread().getName());
        int counter = 1;
        while (isActive) {
            System.out.println("Loop " + counter++);
            try {
                Thread.sleep(400);
            } catch (InterruptedException e) {
                System.out.println("Thread has been interrupted");
            }
        }
        System.out.printf("%s finished... \n", Thread.currentThread().getName());
    }
}

A variável isActive indica se a thread deve continuar executando. O método disable() permite que um código externo altere esse sinalizador.

Nota: Em cenários de concorrência real, é uma boa prática declarar essa flag como volatile para garantir que as alterações feitas por uma thread sejam imediatamente visíveis para as outras.

Agora, vamos utilizar essa classe:

public class Program {

    public static void main(String[] args) {
        System.out.println("Main thread started...");
        MyTask task = new MyTask();
        new Thread(task, "MyTaskThread").start();

        try {
            Thread.sleep(1100);

            task.disable(); // Sinaliza para a thread parar

            Thread.sleep(1000); // Aguarda um pouco para ver a mensagem de finalização
        } catch (InterruptedException e) {
            System.out.println("Thread has been interrupted");
        }
        System.out.println("Main thread finished...");
    }
}

No método main, a thread filha é iniciada. A main thread então pausa por 1100 milissegundos e invoca task.disable(). Na próxima iteração do laço while, a condição isActive será falsa, e a thread filha encerrará sua execução de forma limpa.

O Mecanismo de Interrupção Padrão: O Método interrupt()

Embora a flag booleana funcione, Java oferece um mecanismo padronizado e mais robusto para sinalizar a uma thread que ela deve parar: o método interrupt().

É fundamental entender que chamar interrupt() não finaliza a thread à força. Em vez disso, ele apenas define um status interno na thread para indicar que uma interrupção foi solicitada. Cabe ao código da própria thread verificar cooperativamente esse sinal e agir de acordo, geralmente encerrando sua execução. O método isInterrupted() é usado para essa verificação.

class JThread extends Thread {

    JThread(String name) {
        super(name);
    }

    public void run() {
        System.out.printf("%s started... \n", Thread.currentThread().getName());
        int counter = 1;
        while (!isInterrupted()) {
            System.out.println("Loop " + counter++);
        }
        System.out.printf("%s finished... \n", Thread.currentThread().getName());
    }
}

public class Program {

    public static void main(String[] args) {
        System.out.println("Main thread started...");
        JThread t = new JThread("JThread");
        t.start();
        try {
            Thread.sleep(150);
            t.interrupt(); // Solicita a interrupção da thread t
            Thread.sleep(150);
        } catch (InterruptedException e) {
            System.out.println("Thread has been interrupted");
        }
        System.out.println("Main thread finished...");
    }
}

No laço while, a condição !isInterrupted() verifica o sinalizador de interrupção. Quando t.interrupt() é chamado na main thread, esse sinalizador é ativado, fazendo com que isInterrupted() passe a retornar true, o que causa a saída do laço.

Se a lógica estiver em uma classe que implementa Runnable, a verificação é feita através de Thread.currentThread().isInterrupted().

Lidando com InterruptedException

A complexidade do mecanismo de interrupção surge quando métodos que bloqueiam a execução, como Thread.sleep(), wait() ou join(), são usados dentro do laço. Se uma thread é interrompida enquanto está "dormindo" (bloqueada em sleep), ela não apenas acorda imediatamente, mas também lança uma InterruptedException.

Ao capturar essa exceção, ocorre um detalhe crucial: o status de interrupção da thread é automaticamente resetado para false. A lógica por trás disso é que, ao lançar a InterruptedException, a thread já foi notificada da interrupção através da exceção, então o sinalizador não é mais necessário.

Isso significa que, se não for tratada corretamente, uma chamada subsequente a isInterrupted() retornará false, e o laço continuaria infinitamente.

public void run() {
    System.out.printf("%s started... \n", Thread.currentThread().getName());
    int counter = 1;
    while (!isInterrupted()) {
        System.out.println("Loop " + counter++);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            System.out.println(getName() + " has been interrupted");
            System.out.printf("Is thread interrupted? %s\n", isInterrupted()); // Imprime false

            // Restaura o status de interrupção para que o laço while pare na próxima iteração
            interrupt();
        }
    }
    System.out.printf("%s finished... \n", Thread.currentThread().getName());
}

Como o status de interrupção foi limpo, uma boa prática é chamar interrupt() novamente dentro do bloco catch para restaurá-lo. Assim, na próxima verificação, o laço while terminará corretamente.

Uma alternativa mais direta é sair do laço imediatamente no bloco catch:

while (!isInterrupted()) {
    System.out.println("Loop " + counter++);
    try {
        Thread.sleep(100);
    } catch (InterruptedException e) {
        System.out.println(getName() + " has been interrupted");
        break; // Sai do laço imediatamente
    }
}

Frequentemente, a abordagem mais limpa e segura é colocar todo o laço while dentro de um único bloco try. Dessa forma, quando a InterruptedException for lançada por sleep(), a execução do laço será interrompida e o controle passará para o bloco catch, saindo do try e, consequentemente, finalizando o método run.

public void run() {
    System.out.printf("%s started... \n", Thread.currentThread().getName());
    int counter = 1;
    try {
        while (!isInterrupted()) {
            System.out.println("Loop " + counter++);
            Thread.sleep(100);
        }
    } catch (InterruptedException e) {
        System.out.println(getName() + " has been interrupted while sleeping");
    }
    System.out.printf("%s finished... \n", Thread.currentThread().getName());
}

Resumo

  • Para finalizar threads em laços, pode-se usar uma flag booleana (preferencialmente volatile).
  • O método interrupt() é o mecanismo padrão de Java para solicitar a interrupção de uma thread; ele não força a parada, apenas sinaliza.
  • A thread deve verificar cooperativamente seu estado de interrupção usando isInterrupted().
  • Quando métodos como sleep() lançam InterruptedException, o status de interrupção da thread é limpo.
  • Ao capturar InterruptedException, é crucial ou sair do laço (break) ou restaurar o status de interrupção (interrupt()).
Política de Privacidade

Copyright © www.programicio.com Todos os direitos reservados

É proibida a reprodução do conteúdo desta página sem autorização prévia do autor.

Contato: programicio@gmail.com