Thread Lifecycle in Java: States and Transitions
Introduction
In Java, a thread goes through several states from its creation to termination. Understanding these states and the transitions between them is crucial for effective multithreading programming, helping you manage concurrent tasks efficiently and avoid common pitfalls like deadlocks or thread starvation. This article explores the lifecycle of a thread in Java and discusses the key states it can enter during execution.
Thread Lifecycle
The thread lifecycle is defined by the Thread.State
enum in Java, which contains six major states.
A thread can only exist in one state at a time, and its transition from one state to another is managed by
the Java Virtual Machine (JVM). Here are the key states in the thread lifecycle:
- NEW
- RUNNABLE
- BLOCKED
- WAITING
- TIMED_WAITING
- TERMINATED
These states are triggered by specific events in the lifecycle, such as starting a thread, acquiring locks,
or reaching the end of the run()
method.
1. NEW State
- Definition:
When a thread is created but not yet started, it is in the NEW state. The thread object exists, but it has not been scheduled for execution. - Transition:
The thread moves to the RUNNABLE state when itsstart()
method is called.
1
2
3
Thread t1 = new Thread();
System.out.println(t1.getState()); // Outputs: NEW
t1.start();
2. RUNNABLE State
- Definition:
A thread in the RUNNABLE state is ready to run and is either running on the processor or waiting for CPU time. - Transition:
The thread remains in the RUNNABLE state until it finishes its task, enters a synchronized block (leading to BLOCKED), or waits for some condition (leading to WAITING or TIMED_WAITING).
1
2
3
4
5
6
7
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread is running");
}
}
Thread t1 = new Thread(new MyRunnable());
t1.start();
3. BLOCKED State
- Definition:
A thread enters the BLOCKED state when it attempts to acquire an intrinsic lock (monitor) that another thread is holding. - Transition:
Once the lock is acquired, the thread moves back to the RUNNABLE state.
1
2
3
4
5
6
7
8
9
10
11
public class SynchronizedExample {
public synchronized void method() {
System.out.println("Locked by thread: " + Thread.currentThread().getName());
}
}
SynchronizedExample example = new SynchronizedExample();
Thread t1 = new Thread(() -> example.method());
Thread t2 = new Thread(() -> example.method());
t1.start();
t2.start();
This lambda expression () -> example.method()
is a concise shorthand for an anonymous class implementing Runnable
,
where the run()
method simply calls example.method()
. It simplifies the code by directly defining the action to run,
instead of requiring the full anonymous class syntax.
4. WAITING State
- Definition:
A thread is in the WAITING state when it’s waiting indefinitely for another thread to perform a particular action. This typically occurs when a thread calls methods likeObject.wait()
,Thread.join()
, orLockSupport.park()
. - Transition:
The thread moves back to the RUNNABLE state when another thread callsnotify()
ornotifyAll()
on the monitor it’s waiting on, or when the thread it is waiting to join has completed.
1
2
3
4
5
6
7
Thread t1 = new Thread(() -> {
try {
t2.join(); // t1 waits for t2 to finish
} catch (InterruptedException e) {
e.printStackTrace();
}
});
5. TIMED_WAITING State
- Definition:
A thread is in the TIMED_WAITING state when it’s waiting for another thread or event for a specified amount of time. This happens when you call methods likeThread.sleep()
,Object.wait(timeout)
, orThread.join(timeout)
. - Transition:
After the specified time has elapsed or the condition is met, the thread transitions back to RUNNABLE.
1
Thread.sleep(1000); // Puts the thread in TIMED_WAITING for 1 second
6. TERMINATED State
- Definition:
Once a thread has finished its execution, either by returning from therun()
method or by throwing an uncaught exception, it enters the TERMINATED state. At this point, the thread is no longer eligible to be scheduled for execution. - Transition:
The thread enters this state from RUNNABLE when its task completes.
1
2
3
4
Thread t1 = new Thread(() -> System.out.println("Task complete"));
t1.start();
t1.join(); // Wait for the thread to finish
System.out.println(t1.getState()); // Outputs: TERMINATED
State Transitions
Here’s a summary of common transitions between states:
- NEW -> RUNNABLE: When the
start()
method is called. - RUNNABLE -> BLOCKED: When a thread attempts to enter a synchronized block but the lock is already held.
- RUNNABLE -> WAITING: When a thread calls
Object.wait()
orThread.join()
without a timeout. - RUNNABLE -> TIMED_WAITING: When a thread calls
Thread.sleep()
orwait()/join()
with a timeout. - BLOCKED -> RUNNABLE: When a thread successfully acquires a lock.
- WAITING -> RUNNABLE: When the condition for waiting is met (e.g.,
notify()
is called). - TIMED_WAITING -> RUNNABLE: After the specified wait time has passed.
- RUNNABLE -> TERMINATED: When the thread finishes execution.
Common Thread Issues
Understanding the thread lifecycle helps avoid issues such as:
- Deadlocks: Occurs when two or more threads are blocked forever, each waiting for the other to release a resource.
- Starvation: When a thread is unable to gain regular access to resources and is unable to progress.
- Race Conditions: Occurs when two threads access shared data and try to change it at the same time.
Using proper synchronization mechanisms and understanding thread states allows you to prevent these concurrency problems.
Conclusion
The thread lifecycle in Java provides a structured way to manage concurrent tasks, and knowing the states and transitions can help you write more efficient and bug-free multithreading code. Whether you’re creating a simple background task or managing complex multithreaded systems, understanding the thread lifecycle is a fundamental skill for Java developers.