Thread Lifecycle in Java
A Java thread does not simply jump from "created" to "finished." During its lifetime, it moves through several states depending on whether it has been started, is waiting for CPU time, is blocked by another thread, is waiting for an event, or has completed its work.
Understanding the thread lifecycle is essential because many multithreading problems become much easier to diagnose once you know what a thread is actually doing at a particular moment.
What Is the Thread Lifecycle?
The thread lifecycle describes the different states a Java thread can enter during its existence. Java represents these states through the Thread.State enumeration.
| State | Meaning |
|---|---|
| NEW | The Thread object has been created, but start() has not been called. |
| RUNNABLE | The thread is ready to run or is currently running. |
| BLOCKED | The thread is waiting to acquire a monitor lock. |
| WAITING | The thread is waiting indefinitely for another thread to perform an action. |
| TIMED_WAITING | The thread is waiting for a specified amount of time. |
| TERMINATED | The thread has finished execution. |
Important: Java defines six official thread states. In particular, Java does not expose separate official RUNNING and READY states. Both are represented by RUNNABLE.
Thread Lifecycle at a Glance
NEW | | start() v RUNNABLE | \ | \ | +------------------+ | | | BLOCKED / WAITING | | | TIMED_WAITING | | +-----------------------+ | | run() completes v TERMINATED
The diagram is a simplified view. A thread can move between several states multiple times during its lifetime. For example, a thread may become RUNNABLE, enter BLOCKED, return to RUNNABLE, and eventually become TERMINATED.
1. NEW State
When a Thread object is created but its start() method has not yet been called, the thread is in the NEW state.
Thread thread = new Thread(() -> {
System.out.println("Task is running");
});
System.out.println(thread.getState());
The output is:
NEW
At this point, the Thread object exists, but its execution has not started.
Remember: Creating a Thread object and starting a thread are two different operations.
2. RUNNABLE State
When start() is called, the thread moves from NEW to RUNNABLE.
Thread thread = new Thread(() -> {
System.out.println("Worker is running");
});
thread.start();
The JVM can now schedule the thread for execution. A thread in the RUNNABLE state may be waiting for CPU time or may currently be executing.
This is an important difference from some simplified lifecycle diagrams found in beginner material: Java does not expose a separate RUNNING state through Thread.State.
Why Does Java Combine Ready and Running?
The operating system and JVM scheduler decide when a runnable thread receives CPU time. Java therefore represents both "eligible to run" and "currently executing" under the single RUNNABLE state.
This means you should not interpret RUNNABLE as a guarantee that the thread is actively using the CPU at that exact instant.
3. BLOCKED State
A thread enters the BLOCKED state when it is waiting to acquire a monitor lock so that it can enter a synchronized section or method.
class SharedResource {
public synchronized void process() {
System.out.println("Processing resource");
try {
Thread.sleep(2000);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
If one thread already owns the monitor lock for process(), another thread attempting to enter the same synchronized method may have to wait for that lock. While waiting to acquire the monitor, the second thread can enter the BLOCKED state.
Key distinction: BLOCKED specifically concerns waiting to acquire a monitor lock. It is different from WAITING and TIMED_WAITING.
4. WAITING State
A thread enters WAITING when it waits indefinitely for another thread to perform a particular action.
Methods that can place a thread into WAITING include Object.wait() without a timeout, Thread.join() without a timeout, and LockSupport.park().
Thread worker = new Thread(() -> {
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
worker.start();
try {
worker.join();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
When join() is called without a timeout, the calling thread waits until the target thread terminates. This can place the calling thread into the WAITING state.
5. TIMED_WAITING State
A thread enters TIMED_WAITING when it waits for a specified maximum amount of time.
Common operations associated with TIMED_WAITING include Thread.sleep(), timed join(), timed wait(), and timed locking operations.
Thread thread = new Thread(() -> {
try {
Thread.sleep(3000);
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
thread.start();
During the sleep period, the thread is in TIMED_WAITING. Once the specified time expires, it can become RUNNABLE again.
Easy way to remember: WAITING means "wait until something happens." TIMED_WAITING means "wait until something happens or until the specified time expires."
6. TERMINATED State
When a thread finishes executing its run() method, it enters the TERMINATED state.
Thread thread = new Thread(() -> {
System.out.println("Task completed");
});
thread.start();
try {
thread.join();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println(thread.getState());
After the thread's work has completed, the state is:
TERMINATED
A terminated Thread object cannot be restarted.
Important State Transitions
| Operation | Typical State Transition |
|---|---|
| new Thread(...) | NEW |
| start() | NEW → RUNNABLE |
| Waiting for monitor lock | RUNNABLE → BLOCKED |
| Lock becomes available | BLOCKED → RUNNABLE |
| wait() / join() / park() | RUNNABLE → WAITING |
| sleep() / timed wait / timed join | RUNNABLE → TIMED_WAITING |
| Wait condition completed | WAITING → RUNNABLE |
| Timeout expires | TIMED_WAITING → RUNNABLE |
| run() completes | RUNNABLE → TERMINATED |
Checking a Thread's State
Java provides getState() to inspect the current state of a Thread.
Thread thread = new Thread(() -> {
System.out.println("Task is executing");
});
System.out.println("Before start: "
+ thread.getState());
thread.start();
System.out.println("After start: "
+ thread.getState());
try {
thread.join();
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("After completion: "
+ thread.getState());
The output will commonly resemble:
Before start: NEW After start: RUNNABLE After completion: TERMINATED
However, thread scheduling is nondeterministic. The exact state observed at a particular instant can vary because another thread may have already changed state before getState() is evaluated.
Lifecycle Example with Sleep
The following example makes the transition into TIMED_WAITING easier to visualize.
public class LifecycleDemo {
public static void main(String[] args)
throws InterruptedException {
Thread worker = new Thread(() -> {
try {
System.out.println("Worker started");
Thread.sleep(2000);
System.out.println("Worker finished");
}
catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
System.out.println(
"Initial state: " + worker.getState()
);
worker.start();
Thread.sleep(100);
System.out.println(
"Current state: " + worker.getState()
);
worker.join();
System.out.println(
"Final state: " + worker.getState()
);
}
}
The worker begins in NEW, becomes RUNNABLE after start(), and enters TIMED_WAITING while sleeping. After the sleep completes and the run method finishes, it eventually reaches TERMINATED.
Lifecycle vs Thread Scheduling
A thread's lifecycle state and the operating system's scheduling decisions are related, but they are not the same thing.
For example, two threads can both have the Java state RUNNABLE, while only one may actually be executing on a particular CPU core at that moment.
This is why multithreaded output can change from one execution to another. The scheduler is free to make different scheduling decisions based on system conditions.
Common Beginner Mistakes
- Thinking Java has separate official RUNNING and READY Thread.State values.
- Assuming RUNNABLE always means the thread is currently executing on a CPU.
- Confusing BLOCKED with WAITING.
- Assuming sleep() releases every lock held by a thread.
- Assuming a terminated thread can be started again.
- Expecting getState() to remain constant while another thread is executing.
Best Practices
- Use thread states primarily for understanding, debugging, and diagnosing behavior rather than building fragile application logic around instantaneous state checks.
- Understand why a thread is waiting instead of simply trying to force it back into execution.
- Use synchronization and concurrency utilities deliberately when threads compete for shared resources.
- Handle InterruptedException appropriately and restore the interrupted status when the current layer cannot fully handle the interruption.
- Use thread dumps and monitoring tools in production debugging to identify blocked or waiting threads.
Interview Insights
Question: How many states does a Java thread have?
Java defines six official states in Thread.State: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, and TERMINATED.
Question: What happens after start() is called?
A NEW thread transitions to RUNNABLE. The JVM and operating system then determine when it actually receives CPU time.
Question: What is the difference between BLOCKED and WAITING?
BLOCKED means the thread is waiting to acquire a monitor lock. WAITING means the thread is waiting indefinitely for another thread or event to perform a required action.
Question: What is the difference between WAITING and TIMED_WAITING?
WAITING has no specified timeout, while TIMED_WAITING has a maximum waiting duration.
Question: Can a TERMINATED thread be restarted?
No. A Thread object can be started only once. A new Thread object is required for another execution.
Quick Revision
| State | Remember It As |
|---|---|
| NEW | Created but not started. |
| RUNNABLE | Eligible to run or currently running. |
| BLOCKED | Waiting for a monitor lock. |
| WAITING | Waiting indefinitely for another action. |
| TIMED_WAITING | Waiting for a limited amount of time. |
| TERMINATED | Execution has finished. |
The thread lifecycle gives you a map for understanding what a Java thread is doing throughout its existence. The most important distinction is between the six official states and the operations that move a thread between them. Once you can recognize whether a thread is runnable, blocked, waiting, sleeping, or terminated, debugging multithreaded applications becomes far less mysterious. The next chapter will build on this foundation by exploring the most commonly used Thread methods and what each one actually does.
