A thread in Java goes through various states during its lifecycle, from creation to termination. Understanding the thread lifecycle helps in managing thread behavior and ensuring proper resource handling in multithreaded applications.
Thread States in Java
A thread can be in one of the following states during its lifecycle:
- New:
- Description: A thread is in the “new” state when it is created but has not yet started.
- Example: Thread thread = new Thread(); creates a new thread in the “new” state.
- Transition: The thread will move to the “runnable” state when the start() method is called.
- Runnable:
- Description: A thread is in the “runnable” state when it is ready to run but is waiting for CPU time to be scheduled. Multiple threads can be in the “runnable” state, but only one thread at a time is chosen by the CPU to execute.
- Example: Calling thread.start() moves the thread from “new” to “runnable”.
- Transition: A thread remains in this state until it gets CPU time or is interrupted, yielding, or waiting for a resource.
- Blocked:
- Description: A thread enters the “blocked” state when it is trying to access a synchronized block or method that is locked by another thread. The thread will remain blocked until the lock becomes available.
- Example: synchronized (lockObject) { … } can cause a thread to enter the “blocked” state if the lock is already held by another thread.
- Transition: The thread moves to the “runnable” state once the lock is acquired.
- Waiting:
- Description: A thread is in the “waiting” state when it is waiting indefinitely for another thread to perform a specific action. It will remain in this state until it is awakened by a notification from another thread.
- Example: A thread calls wait() inside a synchronized block.
- Transition: The thread will move to the “runnable” state when notify() or notifyAll() is called on the object it is waiting on.
- Timed Waiting:
- Description: A thread is in the “timed waiting” state when it is waiting for a specified period. It will return to the “runnable” state after the specified time elapses or it is interrupted.
- Example: Calling Thread.sleep(1000), join(1000), or Lock.lock().lockInterruptibly() puts a thread in a timed waiting state.
- Transition: The thread moves to the “runnable” state once the specified time is over or the thread is notified.
- Terminated:
- Description: A thread is in the “terminated” state when it has completed its execution or has been stopped. Once in this state, a thread cannot be started again.
- Example: A thread’s run() method has completed execution, or an exception has caused the thread to terminate.
- Transition: This state is final, and a thread cannot move to any other state once it is terminated.
Transitions Between Thread States
- New to Runnable:
- When start() is called on a thread instance, it transitions from the “new” state to the “runnable” state.
- Runnable to Running:
- The thread scheduler picks a runnable thread to run, moving it from “runnable” to “running”.
- Running to Blocked:
- If a thread tries to access a synchronized resource that is already locked, it transitions to the “blocked” state.
- Running to Waiting:
- A thread calls wait(), join(), or Condition.await() to enter the “waiting” state.
- Running to Timed Waiting:
- A thread calls sleep(milliseconds), join(milliseconds), or Lock.lock().lockInterruptibly() to enter the “timed waiting” state.
- Blocked or Waiting to Runnable:
- When the condition that caused the “blocked” or “waiting” state is met (e.g., lock is released, or notify() is called), the thread transitions back to “runnable”.
- Running to Terminated:
- When the run() method completes or an uncaught exception occurs, the thread moves to the “terminated” state.
Thread State Diagram
The lifecycle of a thread can be visualized in a state diagram:
+——————+
| New |
+——————+
|
v
+——————+
| Runnable |
+——————+
/ | \
/ | \
v v v
Blocked Running Timed Waiting
\ | /
\ | /
v v v
+——————+
| Waiting |
+——————+
|
v
+——————+
| Terminated |
+——————+
Code Example Illustrating Thread Lifecycle
Example:
class LifecycleThread extends Thread {
public void run() {
try {
// Simulate some work
System.out.println(“Thread is running.”);
Thread.sleep(1000); // Moves thread to timed waiting state
} catch (InterruptedException e) {
System.out.println(“Thread was interrupted.”);
}
System.out.println(“Thread has finished execution.”);
}
}
public class ThreadLifecycleExample {
public static void main(String[] args) {
LifecycleThread thread = new LifecycleThread();
System.out.println(“Thread state before start: ” + thread.getState()); // NEW
thread.start(); // Moves to RUNNABLE
System.out.println(“Thread state after start: ” + thread.getState()); // RUNNABLE
try {
thread.join(); // Main thread waits for this thread to finish
} catch (InterruptedException e) {
System.out.println(“Main thread interrupted.”);
}
System.out.println(“Thread state after completion: ” + thread.getState()); // TERMINATED
}
}
Explanation:
- The thread transitions from NEW to RUNNABLE when start() is called.
- The sleep(1000) call puts the thread in the TIMED WAITING state.
- The thread completes execution and moves to the TERMINATED state.
Key Points to Remember
- Thread Scheduler: The JVM’s thread scheduler decides which thread runs at any given time based on scheduling policies.
- Thread Priority: Threads have priorities, which influence their scheduling but do not guarantee the order of execution.
- Synchronization: Proper synchronization is needed to avoid issues like race conditions and deadlocks when accessing shared resources.
- Thread Lifecycle Handling: Be aware of how to handle thread state transitions to avoid issues like resource leaks and unexpected terminations.
Understanding the lifecycle of a thread in Java helps in designing robust multithreaded applications by anticipating and managing the different states a thread can be in throughout its execution.