The Complete Guide to the Java Thread Life Cycle: From Birth to Termination
Understanding the thread life cycle in Java is fundamental for any developer aiming to write efficient, responsive, and concurrent applications. Threads are the lightweight units of execution that enable parallel processing, and their behavior is governed by a well-defined state machine. On top of that, this article provides an in-depth exploration of the Java thread life cycle, breaking down each state, the transitions between them, and practical implications for real-world coding. Whether you are debugging a deadlock or optimizing application performance, mastering these concepts is essential.
The Six States of a Java Thread
Java threads exist in one of six distinct states at any given moment. This leads to these states are defined in the Thread. State enum and determine what the thread is currently doing Small thing, real impact. But it adds up..
- NEW
- RUNNABLE
- BLOCKED
- WAITING
- TIMED_WAITING
- TERMINATED
We will examine each state in detail, along with the conditions that cause a thread to move from one state to another.
1. NEW: The Thread is Born
A thread enters the NEW state when it is instantiated but has not yet started. In real terms, this is the initial state of a thread object. At this point, the thread has not been scheduled for execution by the operating system, and no system resources have been allocated to it The details matter here..
Code Example:
Thread newThread = new Thread(() -> {
System.out.println("Thread is running");
});
// At this moment, newThread is in the NEW state.
To transition out of the NEW state, the thread must be explicitly started using the start() method. Also, calling start() triggers the Java Virtual Machine (JVM) to create the necessary native thread and schedule it for execution. It is a common mistake to call run() directly instead of start(), which would execute the thread's task in the current thread without any concurrent execution.
2. RUNNABLE: Ready to Run
Once a thread is started, it enters the RUNNABLE state. Which means this state has two sub-states: ready and running. A thread in the RUNNABLE state is either actively executing instructions on a CPU or is ready to do so as soon as the CPU becomes available. The JVM's thread scheduler determines when and for how long each runnable thread gets CPU time.
Key Points:
- A thread remains in RUNNABLE while it is executing its
run()method or is waiting for CPU allocation. - This state is often misunderstood. Many developers assume that a thread in RUNNABLE is always running, but it might be waiting for its turn on the CPU.
Transition to Other States: A thread can leave the RUNNABLE state in several ways:
- It calls
sleep(), leading to TIMED_WAITING. - It attempts to enter a synchronized block or method that is currently held by another thread, moving to BLOCKED.
- It calls
wait(),join(), orpark(), which puts it into WAITING or TIMED_WAITING.
3. BLOCKED: Waiting for a Monitor
A thread enters the BLOCKED state when it tries to access a synchronized block or method that is currently being executed by another thread. On top of that, in Java, synchronization is implemented using monitors (or intrinsic locks). Only one thread can hold a monitor at a time. If a thread attempts to acquire a monitor that is already held, it is placed in a queue and moved to the BLOCKED state Not complicated — just consistent. Surprisingly effective..
Code Example:
public class BlockedExample {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
synchronized (lock) {
System.out.Consider this: println("Thread 1 acquired the lock");
try {
Thread. sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().
Thread t2 = new Thread(() -> {
synchronized (lock) {
System.out.println("Thread 2 acquired the lock");
}
});
t1.Worth adding: start();
t2. start();
// t2 will be in BLOCKED state while t1 holds the lock.
**Transition:**
A thread remains **BLOCKED** until the monitor becomes available. Once the holding thread releases the monitor (by exiting the synchronized block), the blocked thread is moved to the **RUNNABLE** state and competes with other threads for the CPU.
---
### 4. WAITING: Indefinite Wait
A thread enters the **WAITING** state when it calls methods like `wait()`, `join()`, or `LockSupport.park()`. Unlike the **BLOCKED** state, which is related to monitor contention, **WAITING** is a state where the thread is *actively* waiting for another thread to perform a specific action. The thread will remain in this state indefinitely until another thread explicitly wakes it up.
**Common Methods that Cause WAITING:**
- `Object.wait()`: Waits until another thread calls `notify()` or `notifyAll()`.
- `Thread.join()`: Waits for the specified thread to die.
- `LockSupport.park()`: Blocks the thread until a permit is available or it is unparked.
**Code Example:**
```java
public class WaitingExample {
private static final Object lock = new Object();
public static void main(String[] args) {
Thread waitingThread = new Thread(() -> {
synchronized (lock) {
try {
System.In practice, out. println("Thread is waiting");
lock.Here's the thing — wait(); // Moves to WAITING state
} catch (InterruptedException e) {
Thread. currentThread().
waitingThread.start();
// Main thread sleeps to ensure waitingThread is in WAITING state
Thread.sleep(100);
synchronized (lock) {
lock.notify(); // Wakes up the waiting thread
}
}
}
Transition: A thread in WAITING can only be moved by:
- Another thread calling
notify()ornotifyAll()on the same object's monitor (forwait()). - The thread being unparked (for
park()). - The specified thread dying (for
join()).
5. TIMED_WAITING: Timed Wait
The TIMED_WAITING state is similar to WAITING, but with a timeout period. A thread enters this state when it calls methods such as sleep(), wait(long timeout), join(long millis), or parkNanos(). The thread will automatically return to the RUNNABLE state once the timeout expires, even if no explicit wake-up occurs Surprisingly effective..
Code Example:
public class TimedWaitingExample {
public static void main(String[] args) {
Thread timedThread = new Thread(() -> {
try {
System.out.println("Thread sleeping for 2 seconds");
Thread.sleep(2000); // Moves to TIMED_WAITING
System.out.println("Thread woke up");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
timedThread.start();
}
}
Transition: The thread leaves TIMED_WAITING when:
- The timeout expires.
- Another thread interrupts it (causing an
InterruptedException). - It is explicitly unparked or notified.
6. TERMINATED: The Thread is Dead
A thread enters the TERMINATED state when its run() method completes normally or throws an exception. Once a thread is terminated, it cannot be restarted. The thread object still exists in memory, but it is no longer a live thread.
Code Example:
public class TerminatedExample {
public static void main(String[] args) {
Thread terminatedThread
```java
public class TerminatedExample {
public static void main(String[] args) {
Thread terminatedThread = new Thread(() -> {
System.out.println("Thread is running");
// Thread completes its task and exits the run() method
});
terminatedThread.Plus, start();
terminatedThread. join(); // Wait for the thread to finish
System.out.println("Thread state after completion: " + terminatedThread.
**Transition:**
The thread enters **TERMINATED** when:
- The `run()` method finishes execution.
- An uncaught exception propagates out of the `run()` method.
---
### Conclusion
Understanding Java's thread lifecycle is crucial for writing solid concurrent applications. Each state—**NEW**, **RUNNABLE**, **BLOCKED**, **WAITING**, **TIMED_WAITING**, and **TERMINATED**—serves a specific purpose in managing thread execution and coordination. By recognizing how threads transition between these states, developers can effectively diagnose concurrency issues, optimize resource usage, and implement reliable synchronization mechanisms. Whether you're building high-performance servers or responsive user interfaces, mastering these foundational concepts ensures your multithreaded code behaves predictably and efficiently.