1. Introducción
En este artículo, discutiremos en detalle un concepto central en Java: el ciclo de vida de un hilo.
Usaremos un diagrama ilustrado rápido y, por supuesto, fragmentos de código prácticos para comprender mejor estos estados durante la ejecución del hilo.
Para comenzar a comprender los subprocesos en Java, este artículo sobre la creación de un subproceso es un buen lugar para comenzar.
2. Subprocesos múltiples en Java
En el lenguaje Java, el subproceso múltiple está impulsado por el concepto central de un subproceso . Durante su ciclo de vida, los subprocesos pasan por varios estados:

3. Ciclo de vida de un hilo en Java
La clase java.lang.Thread contiene una enumeración de estado estática, que define sus estados potenciales. Durante un momento dado, el hilo solo puede estar en uno de estos estados:
- NUEVO: un hilo recién creado que aún no ha comenzado la ejecución
- RUNNABLE: ya sea en ejecución o listo para la ejecución, pero está esperando la asignación de recursos
- BLOQUEADO: esperando adquirir un bloqueo de monitor para ingresar o volver a ingresar un bloque / método sincronizado
- ESPERANDO - esperando a que otro hilo realice una acción en particular sin límite de tiempo
- TIMED_WAITING - esperando que algún otro hilo realice una acción específica durante un período específico
- TERMINADO - ha completado su ejecución
Todos estos estados están cubiertos en el diagrama anterior; discutamos ahora cada uno de estos en detalle.
3.1. Nuevo
Un nuevo hilo (o un hilo nacido ) es un hilo que se ha creado pero que aún no se ha iniciado. Permanece en este estado hasta que lo iniciamos usando el método start () .
El siguiente fragmento de código muestra un hilo recién creado que está en el estado NUEVO :
Runnable runnable = new NewState(); Thread t = new Thread(runnable); Log.info(t.getState());
Como no hemos iniciado el hilo mencionado, el método t.getState () imprime:
NEW
3.2. Runnable
Cuando creamos un nuevo hilo y llamamos al método start () sobre eso, se mueve del estado NEW al RUNNABLE . Los subprocesos en este estado se están ejecutando o están listos para ejecutarse, pero están esperando la asignación de recursos del sistema.
En un entorno de múltiples subprocesos, el Thread-Scheduler (que es parte de JVM) asigna una cantidad fija de tiempo a cada subproceso. Por lo tanto, se ejecuta durante un período de tiempo determinado y luego cede el control a otros subprocesos RUNNABLE .
Por ejemplo, agreguemos el método t.start () a nuestro código anterior e intentemos acceder a su estado actual:
Runnable runnable = new NewState(); Thread t = new Thread(runnable); t.start(); Log.info(t.getState());
Es más probable que este código devuelva el resultado como:
RUNNABLE
Tenga en cuenta que en este ejemplo, no siempre está garantizado que para cuando nuestro control alcance t.getState () , todavía estará en el estado RUNNABLE .
Puede suceder que el Thread-Scheduler lo haya programado inmediatamente y pueda finalizar la ejecución. En tales casos, podemos obtener un resultado diferente.
3.3. Obstruido
Un hilo está en estado BLOQUEADO cuando actualmente no es elegible para ejecutarse. Entra en este estado cuando está esperando un bloqueo de monitor e intenta acceder a una sección de código que está bloqueada por algún otro hilo.
Intentemos reproducir 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 enter this } } }
En este código:
- Hemos creado dos subprocesos diferentes: t1 y t2
- t1 inicia y entra en el método commonResource () sincronizado ; esto significa que solo un hilo puede acceder a él; todos los demás subprocesos posteriores que intenten acceder a este método se bloquearán para la ejecución posterior hasta que el actual termine el procesamiento
- Cuando t1 entra en este método, se mantiene en un ciclo while infinito; esto es solo para imitar un procesamiento pesado para que todos los demás subprocesos no puedan ingresar a este método
- Ahora, cuando iniciamos t2 , intenta ingresar al método commonResource () , al que ya está accediendo t1, por lo tanto, t2 se mantendrá en el estado BLOQUEADO
Estando en este estado, llamamos t2.getState () y obtenemos el resultado como:
BLOCKED
3.4. Esperando
Un hilo está en estado de ESPERA cuando está esperando que otro hilo realice una acción en particular. Según JavaDocs, cualquier hilo puede entrar en este estado llamando a cualquiera de los siguientes tres métodos:
- object.wait ()
- thread.join () o
- LockSupport.park ()
Tenga en cuenta que en wait () y join () , no definimos ningún período de tiempo de espera, ya que ese escenario se trata en la siguiente sección.
Tenemos un tutorial separado que analiza en detalle el uso de esperar () , notificar () y notificar a todos () .
Por ahora, intentemos reproducir 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()); } }
Let's discuss what we're doing here:
- We've created and started the t1
- t1 creates a t2 and starts it
- While the processing of t2 continues, we call t2.join(), this puts t1 in WAITING state until t2 has finished execution
- Since t1 is waiting for t2 to complete, we're calling t1.getState() from t2
The output here is, as you'd expect:
WAITING
3.5. Timed Waiting
A thread is in TIMED_WAITING state when it's waiting for another thread to perform a particular action within a stipulated amount of time.
According to JavaDocs, there are five ways to put a thread on TIMED_WAITING state:
- thread.sleep(long millis)
- wait(int timeout) or wait(int timeout, int nanos)
- thread.join(long millis)
- LockSupport.parkNanos
- LockSupport.parkUntil
To read more about the differences between wait() and sleep() in Java, have a look at this dedicated article here.
For now, let's try to quickly reproduce this state:
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); } } }
Here, we've created and started a thread t1 which is entered into the sleep state with a timeout period of 5 seconds; the output will be:
TIMED_WAITING
3.6. Terminated
This is the state of a dead thread. It's in the TERMINATED state when it has either finished execution or was terminated abnormally.
We have a dedicated article that discusses different ways of stopping the thread.
Let's try to achieve this state in the following example:
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 } }
Here, while we've started thread t1, the very next statement Thread.sleep(1000) gives enough time for t1 to complete and so this program gives us the output as:
TERMINATED
In addition to the thread state, we can check the isAlive() method to determine if the thread is alive or not. For instance, if we call the isAlive() method on this thread:
Assert.assertFalse(t1.isAlive());
Devuelve falso. En pocas palabras, un hilo está vivo si y solo si se ha iniciado y aún no ha muerto.
4. Conclusión
En este tutorial, aprendimos sobre el ciclo de vida de un hilo en Java. Analizamos los seis estados definidos por Thread.State enumeración y los reproducimos con ejemplos rápidos.
Aunque los fragmentos de código darán el mismo resultado en casi todas las máquinas, en algunos casos excepcionales, podemos obtener algunos resultados diferentes ya que no se puede determinar el comportamiento exacto de Thread Scheduler.
Y, como siempre, los fragmentos de código que se utilizan aquí están disponibles en GitHub.