Ciclo de vida de um segmento em Java
1. Introdução
Neste artigo, discutiremos em detalhes um conceito central em Java - o ciclo de vida de um thread.
Usaremos um diagrama ilustrado rápido e, é claro, snippets de código práticos para entender melhor esses estados durante a execução do thread.
Para começar a entender Threads em Java,this article na criação de um thread é um bom lugar para começar.
2. Multithreading em Java
3. Ciclo de vida de um segmento em Java
A classejava.lang.Thread contém umstatic State enum – que define seus estados potenciais. Durante qualquer ponto do tempo, o encadeamento pode estar apenas em um destes estados:
-
NEW – thread recém-criado que ainda não iniciou a execução
-
RUNNABLE – em execução ou pronto para execução, mas está aguardando a alocação de recursos
-
BLOCKED – esperando para adquirir um bloqueio de monitor para entrar ou reentrar em um bloco / método sincronizado
-
WAITING – esperando por algum outro thread para realizar uma ação particular sem qualquer limite de tempo
-
TIMED_WAITING – esperando por algum outro thread para realizar uma ação específica por um período especificado
-
TERMINATED – concluiu sua execução
Todos esses estados são abordados no diagrama acima; vamos agora discutir cada um deles em detalhes.
3.1. New
A NEW Thread (or a Born Thread) is a thread that’s been created but not yet started. Ele permanece neste estado até que o iniciemos usando o métodostart().
O seguinte snippet de código mostra um thread recém-criado que está no estadoNEW:
Runnable runnable = new NewState();
Thread t = new Thread(runnable);
Log.info(t.getState());
Como não iniciamos o segmento mencionado, o métodot.getState() imprime:
NEW
3.2. Executável
Quando criamos um novo thread e chamamos o métodostart() nele, ele é movido do estadoNEW paraRUNNABLE. Threads in this state are either running or ready to run, but they’re waiting for resource allocation from the system.
Em um ambiente multithread, o Agendador de Threads (que faz parte da JVM) aloca uma quantidade fixa de tempo para cada thread. Portanto, ele é executado por um determinado período de tempo e, a seguir, cede o controle a outros encadeamentosRUNNABLE.
Por exemplo, vamos adicionar o métodot.start() ao nosso código anterior e tentar acessar seu estado atual:
Runnable runnable = new NewState();
Thread t = new Thread(runnable);
t.start();
Log.info(t.getState());
Este código émost likely para retornar a saída como:
RUNNABLE
Observe que, neste exemplo, nem sempre é garantido que, quando nosso controle atingirt.getState(), ele ainda estará no estadoRUNNABLE.
Pode acontecer que tenha sido imediatamente agendado pelosThread-Schedulere termine a execução. Nesses casos, podemos obter uma saída diferente.
3.3. Bloqueado
Um encadeamento está no estadoBLOCKED quando atualmente não está qualificado para execução. It enters this state when it is waiting for a monitor lock and is trying to access a section of code that is locked by some other thread.
Vamos tentar reproduzir este estado:
public class BlockedState {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new DemoThreadB());
Thread t2 = new Thread(new DemoThreadB());
t1.start();
t2.start();
Thread.sleep(1000);
Log.info(t2.getState());
System.exit(0);
}
}
class DemoThreadB implements Runnable {
@Override
public void run() {
commonResource();
}
public static synchronized void commonResource() {
while(true) {
// Infinite loop to mimic heavy processing
// 't1' won't leave this method
// when 't2' try to enters this
}
}
}
Neste código:
-
Criamos dois threads diferentes -t1 et2
-
t1 inicia e entra no métodocommonResource() sincronizado; isso significa que apenas um thread pode acessá-lo; todas as outras threads subsequentes que tentarem acessar este método serão bloqueadas para a execução posterior até que a atual termine o processamento
-
Quandot1 entra neste método, ele é mantido em loop while infinito; isso é apenas para imitar o processamento pesado para que todos os outros threads não possam entrar neste método
-
Agora, quando iniciamost2, ele tenta inserir o métodocommonResource(), que já está sendo acessado port1,, portanto,t2 será mantido no estadoBLOCKED
Estando neste estado, chamamost2.getState()e obtemos a saída como:
BLOCKED
3.4. Esperando
A thread is in WAITING state when it’s waiting for some other thread to perform a particular action.According to JavaDocs, qualquer thread pode entrar neste estado chamando qualquer um dos três métodos a seguir:
-
object.wait()
-
thread.join() ou
-
LockSupport.park()
Observe que emwait()ejoin() - não definimos nenhum período de tempo limite, pois esse cenário é coberto na próxima seção.
Temosa separate tutorial que discute em detalhes o uso dewait(),notify()enotifyAll().
Por enquanto, vamos tentar reproduzir este estado:
public class WaitingState implements Runnable {
public static Thread t1;
public static void main(String[] args) {
t1 = new Thread(new WaitingState());
t1.start();
}
public void run() {
Thread t2 = new Thread(new DemoThreadWS());
t2.start();
try {
t2.join();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
}
}
class DemoThreadWS implements Runnable {
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
Log.info(WaitingState.t1.getState());
}
}
Vamos discutir o que estamos fazendo aqui:
-
Nós criamos e iniciamos ot1
-
t1 cria umt2e inicia
-
Enquanto o processamento det2 continua, chamamost2.join(), isso colocat1 no estadoWAITING até quet2 termine a execução
-
Comot1 está esperandot2 terminar, estamos chamandot1.getState() det2
A saída aqui é, como você esperava:
WAITING
3.5. Espera cronometrada
Um encadeamento está no estadoTIMED_WAITING quando está esperando que outro encadeamento execute uma ação específica dentro de um período de tempo estipulado.
According to JavaDocs, existem cinco maneiras de colocar um encadeamento no estadoTIMED_WAITING:
-
thread.sleep(long millis)
-
wait(int timeout) ouwait(int timeout, int nanos)
-
thread.join(long milis)
-
LockSupport.parkNanos
-
LockSupport.parkUntil
Para ler mais sobre as diferenças entrewait()esleep() em Java, dê uma olhada emthis dedicated article here.
Por enquanto, vamos tentar reproduzir rapidamente este estado:
public class TimedWaitingState {
public static void main(String[] args) throws InterruptedException {
DemoThread obj1 = new DemoThread();
Thread t1 = new Thread(obj1);
t1.start();
// The following sleep will give enough time for ThreadScheduler
// to start processing of thread t1
Thread.sleep(1000);
Log.info(t1.getState());
}
}
class DemoThread implements Runnable {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
Log.error("Thread interrupted", e);
}
}
}
Aqui, criamos e iniciamos um threadt1 que entrou no estado de hibernação com um período de tempo limite de 5 segundos; a saída será:
TIMED_WAITING
3.6. Terminado
Este é o estado de um encadeamento morto. It’s in the TERMINATED state when it has either finished execution or was terminated abnormally.
Temosa dedicated article que discute diferentes maneiras de interromper o thread.
Vamos tentar atingir esse estado no exemplo a seguir:
public class TerminatedState implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new TerminatedState());
t1.start();
// The following sleep method will give enough time for
// thread t1 to complete
Thread.sleep(1000);
Log.info(t1.getState());
}
@Override
public void run() {
// No processing in this block
}
}
Aqui, enquanto iniciamos o encadeamentot1, a próxima instruçãoThread.sleep(1000) dá tempo suficiente parat1 ser concluído e, portanto, este programa nos dá a saída como:
TERMINATED
4. Conclusão
Neste tutorial, aprendemos sobre o ciclo de vida de um encadeamento em Java. Vimos todos os seis estados definidos porThread.State enum e os reproduzimos com exemplos rápidos.
Embora os trechos de código tenham a mesma saída em quase todas as máquinas, em alguns casos excepcionais, podemos obter algumas saídas diferentes, pois o comportamento exato do Thread Scheduler não pode ser determinado.
E, como sempre, os fragmentos de código usados aqui sãoavailable on GitHub.