Java’s multithreading capabilities allow developers to create highly concurrent applications. Understanding the thread lifecycle is essential for effectively managing threads and ensuring that your applications perform efficiently and reliably.
In Java, a thread is a lightweight process that runs concurrently with other threads. Each thread has its own stack and local variables but shares memory with other threads in the same process. The Java Virtual Machine (JVM) manages thread execution, scheduling, and lifecycle.
The lifecycle of a thread in Java consists of several states, each representing a specific stage of a thread’s existence. The primary states are:
A thread is in the New state when it is created but not yet started. In this state, the thread has not begun its execution.
public class NewStateExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Thread is running.");
});
// Thread is in New state here
System.out.println("Thread state: " + thread.getState()); // Outputs: NEW
}
}
A thread enters the Runnable state when the start()
method is called. It indicates that the thread is ready to run and waiting for CPU time. A thread in this state may be actively executing or waiting for its turn to execute.
public class RunnableStateExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Running: " + i);
try {
Thread.sleep(500); // Simulating work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
thread.start(); // Thread is now in Runnable state
System.out.println("Thread state after start(): " + thread.getState()); // Outputs: RUNNABLE
}
}
A thread enters the Blocked state when it is trying to acquire a lock that another thread holds. In this state, the thread cannot proceed until the lock becomes available.
class Resource {
public synchronized void access() {
System.out.println(Thread.currentThread().getName() + " is accessing the resource.");
try {
Thread.sleep(2000); // Simulate work
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
public class BlockedStateExample {
public static void main(String[] args) {
Resource resource = new Resource();
Thread thread1 = new Thread(() -> resource.access());
Thread thread2 = new Thread(() -> resource.access());
thread1.start(); // Starts the first thread
thread2.start(); // Starts the second thread, which will be blocked
try {
thread1.join();
thread2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread1 state: " + thread1.getState()); // May output: TERMINATED
System.out.println("Thread2 state: " + thread2.getState()); // Outputs: BLOCKED
}
}
A thread enters the Waiting state when it is waiting for another thread to perform a specific action, such as notify()
or notifyAll()
. In this state, the thread is not consuming CPU resources.
class WaitNotifyExample {
private final Object lock = new Object();
private boolean condition = false;
public void await() throws InterruptedException {
synchronized (lock) {
while (!condition) {
lock.wait(); // Thread enters Waiting state
}
System.out.println("Condition met, proceeding.");
}
}
public void signal() {
synchronized (lock) {
condition = true;
lock.notify(); // Notifies the waiting thread
}
}
}
public class WaitingStateExample {
public static void main(String[] args) {
WaitNotifyExample example = new WaitNotifyExample();
Thread waitingThread = new Thread(() -> {
try {
example.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
waitingThread.start();
try {
Thread.sleep(1000); // Give waitingThread time to enter Waiting state
example.signal(); // Notify the waiting thread
waitingThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Waiting thread state: " + waitingThread.getState()); // Outputs: TERMINATED
}
}
A thread enters the Timed Waiting state when it calls methods like sleep(millis)
, wait(millis)
, or join(millis)
. The thread will remain in this state for the specified duration or until it is notified.
public class TimedWaitingStateExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
Thread.sleep(1000); // Thread enters Timed Waiting state
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("Thread is awake.");
});
thread.start();
System.out.println("Thread state after starting: " + thread.getState()); // Outputs: RUNNABLE
try {
Thread.sleep(500); // Main thread sleeps for a short time
System.out.println("Thread state during sleep: " + thread.getState()); // Likely outputs: TIMED_WAITING
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
A thread enters the Terminated state when it has completed execution, either by returning from the run()
method or by throwing an unhandled exception. In this state, the thread is no longer alive and cannot be restarted.
public class TerminatedStateExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
System.out.println("Thread is running.");
});
thread.start(); // Thread is now running
try {
thread.join(); // Main thread waits for this thread to finish
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread state after completion: " + thread.getState()); // Outputs: TERMINATED
}
}
To summarize, the thread lifecycle can be visualized as follows:
Java provides several methods for managing thread states:
start()
: Transitions a thread from the New state to the Runnable state.run()
: Defines the thread’s execution logic. When called directly, it doesn’t start a new thread.sleep(long millis)
: Puts the thread into the Timed Waiting state for a specified duration.join()
: Causes the current thread to wait until the thread on which join()
is called terminates.wait()
: Causes the current thread to wait until another thread invokes notify()
or notifyAll()
on the object.notify()
: Wakes up a single thread that is waiting on the object’s monitor.notifyAll()
: Wakes up all threads that are waiting on the object’s monitor.interrupt()
: Interrupts a thread, which can be in any state.ConcurrentHashMap
.