POTHIEDU Não Decore a Resposta. Entenda o Caminho

Na Linguagem Java Uma Thread Executavel Pode Entrar

Na Linguagem Java Uma Thread Executavel Pode Entrar

O ciclo de vida real de threads em Java

Quando eu começava a mexer com threads, achava que o conceito de RUNNABLE significava que a thread estava rodando no processador naquele exato momento. Não era esse o caso. RUNNABLE em Java significa algo mais específico: a thread está pronta para executar quando o scheduler do sistema operacional decidir escalá-la. Ela pode estar aguardando CPU, rodando efetivamente, ou mesmo em plena execução de I/O não-bloqueante. Essa distinção importa porque um monte de gente olha para um heap dump e acha que todas as threads RUNNABLE estão consumindo cycles de processamento ativamente. O estado Runnable em Java corresponde ao estado NATIVE, INTERMEDIARY ou QUEUED da JVM. Isso quer dizer que a thread está dentro do stack da JVM aguardando uma thread do kernel para ser escalada pelo SO. Ela pode estar em um run queue, rodando código nativo, ou parada esperando por uma operação de I/O que não bloqueia a thread da JVM. A maioria dos tutoriais simplifica demais isso e vende RUNNABLE como sinônimo de "está rodando", o que gera confusão na hora de analisar deadlocks ou contention.

na linguagem java uma thread executavel pode entrar em quais estados

Além de Runnable, existem quatro outros estados oficiais definidos pela enumeração Thread.State. Um thread pode entrar em New quando você instancia um objeto Thread mas ainda não chamou start(). Nesse estado, ele ainda não é uma thread nativa do sistema operacional. É um objeto Java qualquer que ainda não disparou o processo de criação da thread no kernel. O estado Running não aparece explicitamente na enumeração Thread.State. A JVM agrupa o estado de execução efetiva junto com o estado de prontidão sob o rótulo Runnable. Se você fizer um dump de thread em produção e vir uma thread marcada como Runnable, ela pode estar tanto numa fila de espera por CPU quanto executando código Java ou nativo naquele instante. Quando uma thread executa um método sincronizado e não consegue adquirir o monitor, ela entra no estado Blocked. Isso é diferente de estar simplesmente esperando. Blocked significa especificamente que a thread tentou entrar em um bloco synchronized ou method synchronized e encontrou o monitor ocupado por outra thread. É um estado passivo. A thread não consome CPU enquanto está blocked. Ela apenas aguarda a liberação do lock.

O estado Waiting é entra quando uma thread chama wait() sem timeout, join() sem timeout, ou LockSupport.park() sem parâmetro de tempo. Diferente de Blocked, Waiting não tem relação com disputa por monitor. É uma espera ativa por uma condição que outra thread vai resolver. Uma coisa importante que poucos notam: threads em Waiting não aparecem em métricas de contenção de lock tradicionais. Você precisa analisar waits específicos no código para entender por que elas estão paradas. Timed_Waiting é o equivalente com timeout de Waiting. É o que acontece quando você chama wait(long timeout), sleep(long ms), join(long millis), ou LockSupport.parkNanos(). A thread vai dormir por no máximo o período especificado, mas pode acordar antes se for interrompida ou sinalizada. Esse estado é o mais comum em pools de thread mal configurados, onde threads ficam dormindo em espera por tarefas enquanto o pool cresceu além do necessário. O estado Terminated é final. A thread terminou a execução do método run() ou lançou uma exceção não capturada. Uma vez terminated, não há volta. Você não pode chamar start() novamente numa thread terminated. Tentar fazer isso gera IllegalThreadStateException.

Thread Java | PPT
Thread Java | PPT

Na prática, a transição mais problemática que eu já vi acontecendo em produção envolvia threads que ficavam presas em Waiting. Era um serviço de filas onde consumidores chamavam take() em um BlockingQueue e nunca recebiam sinalização quando a fila recebia items de forma programática. O problema era sutil porque as threads apareciam como Runnable nos métricas padrão do JVM, mas na verdade estavam em Wait_Set dentro do BlockingQueue. A solução foi migrar para um mecanismo de notificação explícita usando Condition do java.util.concurrent.locks, e adicionar um shutdown hook que chamava interrupt() em todas as threads consumidores antes de terminar. Outro detalhe que causa dor de cabeça: quando uma thread em Blocked recebe um interrupt(), ela não entra em Interrupted. Ela lança UnsupportedOperationException em alguns casos, ou simplesmente continua blocked até o lock ser liberado, momento em que a flag de interrupt é recuperada e a exceção é disparada. Isso quebra muitos códigos de shutdown que assumem que interrupt() resolve imediatamente. A transição entre estados também tem uma armadilha com threads daemon. Threads daemon podem terminar a qualquer momento, independentemente do estado em que estiverem. Se você tem threads daemon em Waiting ou Timed_Waiting e a aplicação principal chama System.exit(), essas threads são abortadas sem cleanup. Eu já vi dados sendo corrompidos nesse cenário porque writersdaemon foram matados no meio de uma operação de commit.

Se o seu objetivo é monitoramento sério de threads em produção, esqueça os tools básicos. O jstack mostra o estado, mas não mostra o histórico de transições. Para entender padrões de contenção e waiting, você precisa de algo como JFR (Java Flight Recorder) gravando eventos de thread com timestamp preciso, ou então usar Async Profiler que captura samples de CPU e inclui informações de state transition. Ferramentas como VisualVM ou jconsole dão uma visão estática, mas não ajudam a entender o que aconteceu antes do problema.