Java’s multithreading capabilities are a cornerstone of efficient and responsive applications, allowing developers to perform concurrent tasks within a single program. Understanding the lifecycle of a Java thread is essential for effective thread management, optimal resource utilization, and preventing common issues like deadlock or resource contention. This guide covers each stage in the lifecycle of a Java thread, from its initial creation to its eventual termination, with practical examples and best practices.


Overview of the Java Thread Lifecycle

A Java thread’s lifecycle consists of five main states:

  1. New: When the thread is created but not yet started.
  2. Runnable: When the thread is ready to run but may or may not be actively executing.
  3. Blocked: When the thread is waiting for a monitor lock to enter or re-enter a synchronized block/method.
  4. Waiting: When the thread is waiting indefinitely for another thread’s action.
  5. Timed Waiting: When the thread is waiting for a specified amount of time.
  6. Terminated: When the thread has completed execution.

The transition between these states is managed by Java’s thread management and scheduling system. Here’s a detailed look at each state.


1. The New State

A thread enters the New state when an instance of the Thread class is created but hasn’t started executing. In this state, the thread is only initialized and hasn’t received any CPU time.

Example of Thread Creation (New State)

Java
public class NewThreadExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> System.out.println("Thread is created but not started"));
        // Thread is currently in the New state
    }
}

Transition to Runnable State: The thread will remain in the New state until start() is called. Once start() is invoked, the thread transitions to the Runnable state.


2. The Runnable State

In the Runnable state, a thread is ready to execute but may not necessarily be running at that moment. The Java Virtual Machine (JVM) schedules runnable threads to execute based on priority and availability of CPU resources. A thread in this state might be actively running or queued by the thread scheduler.

Example of Transition to Runnable

Java
public class RunnableExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> System.out.println("Thread is running"));
        thread.start(); // Thread moves to Runnable state
    }
}

3. The Blocked State

A thread enters the Blocked state when it attempts to acquire a lock that is held by another thread. This is common in synchronized blocks and methods. The thread remains in the Blocked state until it successfully acquires the lock.

Example of Blocked State

Java
public class BlockedExample {
    private static final Object LOCK = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(BlockedExample::syncMethod);
        Thread thread2 = new Thread(BlockedExample::syncMethod);

        thread1.start();
        thread2.start();
    }

    public static synchronized void syncMethod() {
        synchronized (LOCK) {
            System.out.println("Thread " + Thread.currentThread().getName() + " has entered the method");
            try {
                Thread.sleep(2000); // Simulate work
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Thread " + Thread.currentThread().getName() + " is leaving the method");
        }
    }
}

Explanation: In this example, if thread1 enters the syncMethod, thread2 will be in a Blocked state until thread1 completes.


4. The Waiting State

A thread is in the Waiting state when it’s waiting indefinitely for another thread to perform a specific action, such as notifying or interrupting it. A thread enters the Waiting state by calling methods like Object.wait() or Thread.join().

Example of Waiting State

Java
public class WaitingExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(1000);
                System.out.println("Thread finished execution");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        thread.start();

        try {
            thread.join(); // Main thread goes into Waiting state until 'thread' finishes
            System.out.println("Main thread resumed after waiting");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Explanation: Here, the main thread enters the Waiting state using join() until the child thread completes.


5. The Timed Waiting State

A thread enters the Timed Waiting state when it’s waiting for a specified period. Methods like sleep(long millis), wait(long timeout), and join(long timeout) put threads in this state. Once the specified time elapses, the thread moves back to the Runnable state.

Example of Timed Waiting State

Java
public class TimedWaitingExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            try {
                Thread.sleep(3000); // Thread enters Timed Waiting for 3 seconds
                System.out.println("Thread resumed after timed waiting");
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });

        thread.start();
    }
}

Explanation: In this case, the thread goes into Timed Waiting for 3 seconds.


6. The Terminated State

The Terminated state is the final state of a thread’s lifecycle. A thread enters this state when it has completed executing or if it’s forcibly terminated. Once terminated, a thread cannot be restarted.

Example of Terminated State

Java
public class TerminatedExample {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> System.out.println("Thread is running"));
        thread.start();

        try {
            thread.join(); // Ensures the thread completes execution
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        System.out.println("Thread has terminated");
    }
}

Best Practices for Managing Thread Lifecycle

  1. Use Synchronization Carefully: Avoid deadlocks by managing locks and synchronizations carefully.
  2. Handle InterruptedException Gracefully: Always handle InterruptedException to maintain smooth thread flow.
  3. Avoid Busy Waiting: Avoid using loops to check conditions, and prefer methods like wait() or sleep() to free up CPU resources.
  4. Use Executors Framework: For complex applications, consider the Executors framework instead of manual thread management.

FAQs on Java Thread Lifecycle

  1. What is the Java Thread Lifecycle?
    The Java thread lifecycle consists of New, Runnable, Blocked, Waiting, Timed Waiting, and Terminated states.
  2. How does a thread transition from New to Runnable?
    A thread transitions from New to Runnable when the start() method is called.
  3. What is the difference between Waiting and Timed Waiting?
    In Waiting, a thread waits indefinitely, while in Timed Waiting, it waits for a specific duration.
  4. What causes a thread to enter the Blocked state?
    A thread enters the Blocked state if it tries to enter a synchronized block/method held by another thread.
  5. Can a thread be restarted after it terminates?
    No, once a thread terminates, it cannot be restarted.
  6. What is the purpose of Thread.join()?
    join() is used to make the current thread wait until another thread completes.
  7. When should I use Runnable over Thread for thread management?
    Runnable is preferred for flexibility and modularity, especially when multiple threads share the same task.
  8. What is the Executors framework?
    Executors is a high-level framework in Java for managing thread pools and tasks efficiently.
  9. What happens if start() is called on a thread multiple times?
    Calling start() multiple times on a thread causes an IllegalThreadStateException.
  10. Is it safe to use sleep() for pausing a thread?
    Yes, but it’s important to handle InterruptedException and avoid frequent use in production environments.

External Resources for Further Learning

For more information, check these resources:


By understanding and mastering the Java thread lifecycle, Java professionals can develop robust, efficient, and responsive applications. Proper thread management not only improves performance but also minimizes issues related to concurrency, leading to cleaner and more maintainable code.