🧵 Life Cycle of a Thread in Java: From Birth to Termination
Multithreading is a powerful feature in Java that allows concurrent execution of two or more threads for maximum CPU utilization. At the core of multithreading lies the concept of a thread life cycle. Understanding how a thread moves from one state to another is key to writing robust, efficient, and maintainable multithreaded applications.
In this blog post, we will explore the complete life cycle of a thread in Java, covering each state in detail, their transitions, and the significance of each in real-world applications.
🚦 What is a Thread?
In Java, a thread is a lightweight subprocess, the smallest unit of processing. Java provides built-in support for multithreaded programming via the Thread
class and the Runnable
interface.
A thread is an independent path of execution within a program. When a Java application starts, one thread begins executing — the main
thread. From there, the application can create additional threads.
🔄 Life Cycle of a Thread
A thread in Java can exist in one of the following six states:
-
New
-
Runnable
-
Running
-
Blocked
-
Waiting / Timed Waiting
-
Terminated (Dead)
These states are defined in the
Thread.State
enum since Java 5.
Let’s dive into each one with a diagram and explanation.
📌 1. New State
A thread is in the New state when an instance of the Thread
class is created, but the start()
method has not been called yet.
Thread t1 = new Thread(() -> {
System.out.println("Thread running");
});
At this point, t1
is in the New state.
🔁 Transition to Runnable
Once we invoke t1.start()
, the thread transitions to the Runnable state.
📌 2. Runnable State
In this state, the thread is ready to run and waiting for CPU time. It is not actually running yet. The thread scheduler decides which thread from the runnable pool should be executed.
t1.start(); // Now thread is in Runnable state
Java doesn’t guarantee the order of thread execution — it's up to the Thread Scheduler, which may vary by OS and JVM.
📌 3. Running State
A thread moves to the Running state when the thread scheduler selects it and starts executing its task. This is the state where the thread's run()
method is being executed.
Note: A thread can transition between Runnable and Running multiple times.
📌 4. Blocked State
A thread enters the Blocked state when it tries to access a synchronized resource that is currently locked by another thread.
synchronized (resource) {
// thread holds the lock
}
If another thread tries to acquire the same lock, it becomes Blocked until the lock is released.
📌 5. Waiting and Timed Waiting
These two states occur when a thread is paused, waiting for another thread to perform a specific action.
✅ Waiting State
A thread is in Waiting state when it waits indefinitely for another thread to signal it to continue.
thread.join(); // Waiting for thread to die
object.wait(); // Waiting to be notified
⏱️ Timed Waiting
This state is similar to waiting, but with a timeout.
Thread.sleep(500); // Sleep for 500 ms
object.wait(1000); // Wait for 1 second
thread.join(2000); // Wait max 2 seconds for thread to die
📌 6. Terminated (Dead) State
A thread is in the Terminated or Dead state when it completes its execution or is explicitly stopped.
// Execution complete or uncaught exception occurred
System.out.println("Thread completed");
A terminated thread cannot be started again. Calling
start()
on a dead thread throwsIllegalThreadStateException
.
🔄 Thread Life Cycle Diagram
+---------+
| New |
+---------+
|
| start()
v
+----------+ preempted +---------+
| Runnable |<--------------------->| Running |
+----------+ +---------+
| |
| | complete/run() ends
| v
+--------------+ +----------+
| TimedWaiting|<--sleep(),join()--| |
+--------------+ | Terminated|
^ +----------+
|
+--------------+ <---wait() / join() with no timeout
| Waiting |
+--------------+
^
|
+--------------+ <---waiting for monitor lock (synchronized)
| Blocked |
+--------------+
🛠️ Practical Examples
Example: Thread Waiting and Notification
class Shared {
synchronized void produce() throws InterruptedException {
wait(); // Thread will enter waiting state
}
synchronized void consume() {
notify(); // Wakes up waiting thread
}
}
This is common in producer-consumer problems, where one thread waits for a condition, and another thread notifies it when the condition is met.
⚠️ Key Considerations
-
Never call
run()
directly — usestart()
to begin a new thread. -
Once a thread enters the Terminated state, it cannot be restarted.
-
Avoid using deprecated methods like
stop()
andsuspend()
— they are unsafe and can leave shared resources in an inconsistent state. -
Use
ExecutorService
and higher-level APIs likeCompletableFuture
for better thread management in modern Java.
🧠 Conclusion
Understanding the life cycle of a thread helps you write better multithreaded applications, debug issues effectively, and avoid common pitfalls like deadlocks and race conditions.
Multithreading is a vast topic, but mastering thread life cycle is one of the first solid steps. Java’s built-in support for threads provides great flexibility, but it comes with responsibility. Use thread synchronization mechanisms wisely, always be aware of thread states, and monitor thread behavior during development.
Sign up here with your email
ConversionConversion EmoticonEmoticon