Java Thread Lifecycle
A thread can be created, scheduled, blocked, waiting, or finished. Thread.State names the states Java exposes, but a state reading is only a snapshot. The examples connect these states to actions such as start(), sleep(), waiting for a lock, and completing run().
What is the Thread Lifecycle?
The thread lifecycle describes the different states a thread can be in during its lifetime.
A thread can be in one of the following six states:
- NEW
- RUNNABLE
- BLOCKED
- WAITING
- TIMED_WAITING
- TERMINATED
These states are defined by the Thread.State enum.
Thread Lifecycle Diagram
The basic lifecycle can be understood as:
Thread Created
|
v
NEW
|
start()
|
v
RUNNABLE
/ | \
/ | \
v v v
BLOCKED WAITING TIMED_WAITING
\ | /
\ | /
\ | /
\ | /
RUNNABLE
|
|
run() completes
|
v
TERMINATEDA thread does not necessarily move through every state. Its state depends on what the thread is doing.
1. NEW State
A thread is in the NEW state when a Thread object has been created but its start() method has not yet been called.
Example
Thread thread = new Thread(() -> {
System.out.println("Task is running");
});At this point, the thread has been created, but it has not started.
We can check its state:
System.out.println(thread.getState());Output
NEWThe thread remains in the NEW state until start() is called.
2. RUNNABLE State
When start() is called, the thread moves from NEW to RUNNABLE.
thread.start();The thread is now eligible to run.
Example
Thread thread = new Thread(() -> {
System.out.println("Task is running");
});
System.out.println(thread.getState());
thread.start();The state before calling start() is NEW.
After starting, the thread enters the RUNNABLE state.
What Does RUNNABLE Mean?
The RUNNABLE state means that the thread is either:
- ready to run, or
- currently running.
Java combines these conditions into the single RUNNABLE state.
For example, a thread may be ready to execute but waiting for CPU time from the operating system.
Therefore, Java does not have a separate RUNNING state in Thread.State.
3. BLOCKED State
A thread enters the BLOCKED state when it is waiting to acquire a monitor lock in order to enter a synchronized block or method.
Consider this example:
class Counter {
public synchronized void increment() {
for (int i = 0; i < 5; i++) {
System.out.println(
Thread.currentThread().getName()
);
}
}
}Suppose two threads try to execute the synchronized method at the same time.
Only one thread can acquire the object's monitor lock.
The other thread must wait for the lock.
That waiting thread can enter the BLOCKED state.
Conceptually:
Thread-1 → owns lock → executing
Thread-2 → waiting for lock → BLOCKEDWhen Thread-1 releases the lock, Thread-2 can compete for the lock and continue.
4. WAITING State
A thread enters the WAITING state when it waits indefinitely for another thread or action to occur.
Some methods that can cause a thread to enter WAITING include:
- Object.wait()
- Thread.join()
- LockSupport.park()
For example:
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
thread.start();
thread.join();Here, the thread that calls join() waits until the specified thread finishes.
WAITING Example with wait()
A thread can also enter the WAITING state by calling wait().
synchronized (lock) {
lock.wait();
}The thread waits until another thread performs an appropriate notification, such as:
synchronized (lock) {
lock.notify();
}or:
synchronized (lock) {
lock.notifyAll();
}The exact behavior depends on how the threads coordinate.
5. TIMED_WAITING State
A thread enters the TIMED_WAITING state when it waits for a specified amount of time.
Common methods that can cause this state include:
- Thread.sleep()
- Object.wait(timeout)
- Thread.join(timeout)
- LockSupport.parkNanos()
- LockSupport.parkUntil()
Example using sleep()
Thread thread = new Thread(() -> {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Task completed");
});
thread.start();While the thread is sleeping, it is in the TIMED_WAITING state.
After the specified time expires, the thread becomes eligible to run again.
WAITING vs TIMED_WAITING
The main difference is whether a time limit is specified.
| WAITING | TIMED_WAITING |
|---|---|
| Waits indefinitely | Waits for a specified time |
| No timeout | Has a timeout |
| wait() | wait(timeout) |
| join() | join(timeout) |
| LockSupport.park() | Thread.sleep() |
| Requires another event/action to continue | Can continue automatically when the timeout expires |
For example:
thread.join();The calling thread waits until the target thread finishes.
Whereas:
thread.join(2000);The calling thread waits for at most approximately 2 seconds, unless the target thread finishes earlier.
6. TERMINATED State
A thread enters the TERMINATED state after its execution has completed.
For example:
Thread thread = new Thread(() -> {
System.out.println("Task completed");
});
thread.start();After the run() method finishes, the thread is terminated.
We can check this using:
System.out.println(thread.getState());The output will be:
TERMINATEDA terminated thread cannot be started again.
Complete Thread Lifecycle Example
The following example demonstrates several thread states:
public class HelloWorld {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Task completed");
});
System.out.println("Before start: "
+ thread.getState());
thread.start();
System.out.println("After start: "
+ thread.getState());
Thread.sleep(500);
System.out.println("While sleeping: "
+ thread.getState());
thread.join();
System.out.println("After completion: "
+ thread.getState());
}
}A possible output is:
Before start: NEW
After start: RUNNABLE
While sleeping: TIMED_WAITING
Task completed
After completion: TERMINATEDThe exact state observed at a particular instant can vary because thread scheduling is not deterministic.
Thread State Using getState()
Java provides the getState() method to determine the current state of a thread.
Syntax:
thread.getState();It returns a value of type Thread.State.
Example
Thread thread = new Thread(() -> {
System.out.println("Running");
});
System.out.println(thread.getState());
thread.start();
System.out.println(thread.getState());Possible output:
NEW
RUNNABLEThe state can change quickly, so the returned value represents the thread's state at the moment getState() is called.
Thread.State Enum
Java defines the thread states using the nested enum Thread.State.
The six constants are:
- Thread.State.NEW
- Thread.State.RUNNABLE
- Thread.State.BLOCKED
- Thread.State.WAITING
- Thread.State.TIMED_WAITING
- Thread.State.TERMINATED
Example
Thread.State state = thread.getState();
if (state == Thread.State.RUNNABLE) {
System.out.println("Thread is runnable");
}Thread Lifecycle and start()
The start() method is responsible for transitioning a newly created thread from NEW into execution.
Example
Thread thread = new Thread(() -> {
System.out.println("Running");
});
System.out.println(thread.getState()); // NEW
thread.start();A thread should be started using thread.start(); not thread.run();.
Calling run() directly does not start a new thread.
Thread Lifecycle and sleep()
When a running thread calls:
Thread.sleep(2000);it enters the TIMED_WAITING state for the specified duration, subject to interruption and scheduling.
Example
Thread thread = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});After the waiting period ends, the thread becomes eligible to run again.
Thread Lifecycle and join()
The join() method causes the thread that calls it to wait for another thread to finish.
Example
Thread thread = new Thread(() -> {
System.out.println("Worker thread");
});
thread.start();
thread.join();Here, the thread that calls join() waits until thread terminates.
The important point is that join() affects the thread that calls join().
Thread Lifecycle and Synchronization
Synchronization can cause a thread to enter the BLOCKED state.
Example
class Test {
synchronized void display() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}If one thread is executing display() and another thread tries to enter the same synchronized method on the same object, the second thread may become BLOCKED while waiting to acquire the object's monitor.
Important Difference Between BLOCKED and WAITING
These two states are often confused.
BLOCKED
A thread is waiting to acquire a monitor lock.
Thread
↓
Needs synchronized lock
↓
Lock unavailable
↓
BLOCKEDWAITING
A thread is waiting for another thread or action to perform a particular event.
Thread
↓
wait() / join() / park()
↓
WAITINGSo:
BLOCKED → waiting for a monitor lock
WAITING → waiting for another thread/actionImportant Difference Between RUNNABLE and RUNNING
Java does not define a separate RUNNING state.
The Thread.State enum contains:
- NEW
- RUNNABLE
- BLOCKED
- WAITING
- TIMED_WAITING
- TERMINATED
When a thread is actively executing or is ready to be scheduled, Java represents it using RUNNABLE.
The operating system is responsible for actual CPU scheduling.
Can a Thread Move Back to RUNNABLE?
Yes. A thread can move between several states during its lifecycle.
For example:
NEW
↓
RUNNABLE
↓
TIMED_WAITING
↓
RUNNABLE
↓
TERMINATEDAnother possible sequence is:
NEW
↓
RUNNABLE
↓
BLOCKED
↓
RUNNABLE
↓
TERMINATEDA thread does not necessarily visit every state.
Thread Lifecycle Example with Multiple States
Consider:
class MyTask implements Runnable {
@Override
public void run() {
try {
System.out.println("Task started");
Thread.sleep(2000);
System.out.println("Task finished");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public class HelloWorld {
public static void main(String[] args)
throws InterruptedException {
Thread thread = new Thread(new MyTask());
System.out.println(
"State: " + thread.getState()
);
thread.start();
Thread.sleep(500);
System.out.println(
"State: " + thread.getState()
);
thread.join();
System.out.println(
"State: " + thread.getState()
);
}
}A possible lifecycle is:
NEW
↓
start()
↓
RUNNABLE
↓
TIMED_WAITING
↓
RUNNABLE
↓
TERMINATEDThread Lifecycle Summary
| State | Meaning |
|---|---|
| NEW | Thread object created but not started |
| RUNNABLE | Thread is ready to run or running |
| BLOCKED | Waiting to acquire a monitor lock |
| WAITING | Waiting indefinitely for another thread/action |
| TIMED_WAITING | Waiting for a specified period |
| TERMINATED | Thread execution has completed |
Key Takeaways
- Every Java thread has a lifecycle.
- Java defines six thread states.
- A newly created thread starts in the NEW state.
- Calling start() moves the thread into the RUNNABLE state.
- RUNNABLE represents both ready-to-run and actively running conditions.
- BLOCKED means the thread is waiting to acquire a monitor lock.
- WAITING means the thread is waiting indefinitely for another action.
- TIMED_WAITING means the thread is waiting for a specified period.
- A completed thread enters the TERMINATED state.
- getState() can be used to check a thread's current state.
- A thread does not necessarily pass through every state.
- Thread state can change quickly because of scheduling.
- A terminated thread cannot be started again.
Simple Lifecycle to Remember
create Thread
↓
NEW
↓
start()
↓
RUNNABLE
↙ ↓ ↘
BLOCKED WAITING TIMED_WAITING↘ ↓ ↙ RUNNABLE ↓ run() completes ↓ TERMINATED
The key idea is: The thread lifecycle describes how a thread moves from creation to execution, temporary waiting or blocking, and finally completion.
See you in next page....🙂