CodeGym /Cursos /JAVA 25 SELF /Estados e ciclo de vida da thread

Estados e ciclo de vida da thread

JAVA 25 SELF
Nível 51, Lição 2
Disponível

1. Estados principais da thread em Java

Em Java, uma thread não é apenas “iniciada” ou “parada”. Ela tem todo um ciclo de vida, e em cada etapa se comporta de maneira diferente. Entender essas etapas — a chave para escrever aplicações multithread estáveis e para depurar estranhos “travamentos” e “encerramentos inesperados”.

Quais são os estados?

O Java define os seguintes estados principais de thread (eles estão listados no enum Thread.State):

Estado Descrição
NEW
A thread foi criada, mas ainda não foi iniciada (new Thread(...), mas start() não foi chamado)
RUNNABLE
A thread está pronta para execução ou está executando agora
BLOCKED
A thread aguarda a liberação do monitor (bloqueada em um bloco synchronized)
WAITING
A thread aguarda até que outra thread a “acorde” (por exemplo, via Object.wait())
TIMED_WAITING
A thread aguarda com timeout (por exemplo, Thread.sleep(1000), wait(1000), join(1000))
TERMINATED
A thread concluiu a execução

Fato interessante:
Em livros e artigos antigos, você pode encontrar outros nomes ou esquemas um pouco diferentes. Mas a partir do Java 5+, esses estados são considerados o padrão.

Esquema visual do ciclo de vida da thread

stateDiagram-v2
    [*] --> NEW
    NEW --> RUNNABLE: start()
    RUNNABLE --> BLOCKED: tentativa de entrar em synchronized, mas o monitor está ocupado
    BLOCKED --> RUNNABLE: monitor liberado
    RUNNABLE --> WAITING: wait(), join()
    RUNNABLE --> TIMED_WAITING: sleep(), wait(timeout), join(timeout)
    WAITING --> RUNNABLE: notify()/notifyAll(), join() finalizado
    TIMED_WAITING --> RUNNABLE: timeout expirou / notify()/notifyAll()
    RUNNABLE --> TERMINATED: run() terminou
    WAITING --> TERMINATED: run() terminou (raramente)
    TIMED_WAITING --> TERMINATED: run() terminou (raramente)

2. Métodos de controle de thread

Sono: Thread.sleep(long millis)

Às vezes, a thread precisa “dormir” para não atrapalhar outras ou para esperar um evento. O método Thread.sleep(ms) coloca a thread no estado

TIMED_WAITING
pelo número de milissegundos especificado.

System.out.println("A thread vai dormir por 2 segundos...");
Thread.sleep(2000); // Dormimos por 2 segundos
System.out.println("A thread acordou!");
  • Após o término do sono, a thread volta ao estado
    RUNNABLE
    (pronta para trabalhar).
  • Se a thread for interrompida durante o sono, é lançada InterruptedException.

Aguardar o término de outra thread: join()

Às vezes não basta apenas iniciar uma thread; é preciso esperar ela terminar. Para isso, em Java existe o método join():

Thread t = new Thread(() -> {
    System.out.println("Trabalhando...");
    try { Thread.sleep(1000); } catch (InterruptedException e) {}
    System.out.println("Pronto!");
});
t.start();
System.out.println("Aguardando a conclusão da thread t...");
t.join(); // A thread atual (por exemplo, main) espera t
System.out.println("A thread t foi concluída!");

Aqui, a thread principal começa a esperar assim que join() é chamado. Durante todo esse tempo ela fica no estado

WAITING
, até que a thread t termine a execução. Há também a variante com timeout — join(long millis). Nesse caso, a thread espera por um tempo limitado e seu estado será
TIMED_WAITING
.

Interrupção de thread: interrupt()

Às vezes é preciso pedir educadamente para a thread encerrar o trabalho, por exemplo, se o usuário clicou em “Cancelar”. Para isso, usa-se o método interrupt():

Thread t = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        // Trabalhando...
    }
    System.out.println("Thread encerrada por interrupção!");
});
t.start();
// ... depois de um tempo:
t.interrupt(); // Sinal para a thread: "é hora de parar"

É importante entender que chamar interrupt() não mata a thread instantaneamente. Ele apenas define uma flag especial. A própria thread deve, de tempos em tempos, verificar essa flag por meio de isInterrupted() e encerrar-se sozinha. Se, nesse momento, a thread estiver dormindo (sleep()) ou aguardando (wait()), ela não apenas verá a flag, mas imediatamente receberá a exceção InterruptedException. É assim que funciona em Java a forma “correta” de parar threads: o programa não as interrompe à força, apenas notifica que é hora de encerrar.

3. Exemplos de transições entre estados

Vamos ver, com exemplos, como a thread “viaja” entre seus estados.

Exemplo 1:
NEW
→
RUNNABLE
→
TERMINATED

Thread t = new Thread(() -> System.out.println("Olá!"));
System.out.println(t.getState()); // NEW
t.start();
System.out.println(t.getState()); // RUNNABLE (ou TERMINATED, se a thread for muito rápida)
t.join();
System.out.println(t.getState()); // TERMINATED

Exemplo 2:
RUNNABLE
→
TIMED_WAITING
→
RUNNABLE
→
TERMINATED

Thread t = new Thread(() -> {
    try {
        System.out.println("Vou dormir...");
        Thread.sleep(1000); // TIMED_WAITING
        System.out.println("Acordei!");
    } catch (InterruptedException e) {
        System.out.println("Thread interrompida!");
    }
});
t.start();

Exemplo 3:
RUNNABLE
→
WAITING
com join()

Thread t1 = new Thread(() -> {
    try { Thread.sleep(500); } catch (InterruptedException ignored) {}
    System.out.println("t1 finalizada");
});
Thread t2 = new Thread(() -> {
    try {
        t1.join(); // t2 espera por t1, está em WAITING
        System.out.println("t2 aguardou t1");
    } catch (InterruptedException ignored) {}
});
t1.start();
t2.start();

Exemplo 4:
BLOCKED

Object lock = new Object();

Thread t1 = new Thread(() -> {
    synchronized (lock) {
        try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
        System.out.println("t1 saiu do bloco synchronized");
    }
});
Thread t2 = new Thread(() -> {
    synchronized (lock) {
        System.out.println("t2 entrou em synchronized");
    }
});
t1.start();
Thread.sleep(100); // Vamos permitir que t1 adquira o lock
t2.start();
Thread.sleep(100); // Vamos permitir que t2 tente entrar em synchronized
System.out.println("Estado de t2: " + t2.getState()); // BLOCKED

4. Como saber o estado da thread? Métodos isAlive() e getState()

  • isAlive() — retorna true se a thread foi iniciada e ainda não terminou (
    TERMINATED
    — já é false).
  • getState() — retorna o estado atual da thread (um valor do enum Thread.State).
Thread t = new Thread(() -> {});
System.out.println(t.isAlive()); // false (NEW)
t.start();
System.out.println(t.isAlive()); // true (RUNNABLE/WAITING/...)
t.join();
System.out.println(t.isAlive()); // false (TERMINATED)

5. Por que não é possível “matar” uma thread diretamente e outras dicas práticas

Não existe método “kill”!

Na linguagem Java não há um método que permita “matar” uma thread por comando. Por quê? Porque isso é inseguro: se a thread estiver segurando algum recurso (arquivo, conexão, bloqueio), encerrá-la à força pode deixar o sistema em estado inconsistente.

Métodos obsoletos: stop(), suspend(), resume()

Em versões antigas do Java existiam os métodos stop(), suspend(), resume(). Hoje eles estão marcados como @Deprecated, e seu uso é terminantemente desencorajado. Por quê?

  • stop() pode matar a thread a qualquer momento, deixando os dados em estado inconsistente.
  • suspend() pode “congelar” uma thread que detém um bloqueio, e então todo o programa trava.
  • resume() às vezes não consegue “descongelar” a thread, caso ela já tenha terminado.

Abordagem moderna:
A thread deve encerrar-se corretamente por conta própria, reagindo à flag de interrupção (isInterrupted()) ou a outros sinais.

Boas práticas

  • Não chame métodos marcados como obsoletos.
  • Use a flag de interrupção para parar a thread (interrupt() e a verificação isInterrupted()).
  • Observe os estados das threads durante a depuração — isso ajuda a encontrar travamentos e deadlocks.
  • Não se esqueça de join() se precisar aguardar a conclusão do trabalho da thread.

6. Erros típicos ao trabalhar com o ciclo de vida da thread

Erro nº 1: iniciar a thread novamente.
Em Java, uma thread só pode ser iniciada uma única vez. Se você chamar start() pela segunda vez — receberá IllegalThreadStateException. Se precisar repetir a tarefa, crie um novo objeto Thread.

Thread t = new Thread(() -> {});
t.start();
t.start(); // Vai lançar uma exceção!

Erro nº 2: confundir run() e start().
Chamar run() diretamente não executa o código em uma nova thread — ele é executado na thread atual (por exemplo, em main). Somente start() realmente inicia uma nova thread.

Erro nº 3: não tratar InterruptedException.
Se a thread estiver dormindo ou aguardando e for interrompida, será lançada InterruptedException. Se ignorá-la, a thread pode “dormir para sempre” ou terminar inesperadamente.

Erro nº 4: não verificar o estado da thread.
Às vezes o programa trava porque uma thread espera por outra que já terminou ou nunca vai iniciar. Use getState() e isAlive() para diagnosticar.

Erro nº 5: usar métodos obsoletos de controle de thread.
Os métodos stop(), suspend(), resume() — são problemáticos. Não os use, mesmo que dê muita vontade de “consertar tudo rápido”.

1
Tarefa
JAVA 25 SELF, nível 51, lição 2
Bloqueado
Controle de conclusão da missão espacial 🛰️
Controle de conclusão da missão espacial 🛰️
1
Tarefa
JAVA 25 SELF, nível 51, lição 2
Bloqueado
Sincronização do trabalho de dois colaboradores 🤝
Sincronização do trabalho de dois colaboradores 🤝
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION