Thread Life Cycle in Java

Jayita SahaJayita Saha
5 min read

Introduction

By now, I hope you have a clear and concise understanding of threads and how we can create threads in Java. In this blog, we will discuss the lifecycle of a thread in Java. Yes, you read it correctly! A thread also has its own lifecycle, or we can say a thread can have different states, just like humans have different states (running, waiting, sleeping, etc.). Today, we will understand the lifecycle of a thread.

States of a Thread

From the creation of a thread to its execution and from execution to termination/completion, a thread transitions through different states. In Java, the Thread class has a static enum called State, where six different states are defined. At any point in its lifecycle, a thread can be in one of these six states:

  1. NEW State

  2. RUNNABLE State

  3. WAITING State

  4. TIMED_WAITING State

  5. BLOCKED State

  6. TERMINATED State

Let’s understand each state with code examples.


NEW State

This is the stage when a thread is created but has not yet started. If you remember from the thread creation blog, a thread will start executing code only after calling the start() method. So, after defining or creating a thread, until we call the start() method, that thread remains in its NEW state.

Example:

Runnable runnable = new NewState();
Thread t = new Thread(runnable);
System.out.println(t.getState());

In this code snippet, we have created a thread t but haven’t called the start() method yet. That’s why, when we call getState(), it will return NEW as the output.


RUNNABLE State

This state represents when a thread is ready to run or is already running (executing code), but it is waiting for system resource allocation. A thread moves from the NEW state to the RUNNABLE state when we create a thread and call the start() method.

Example:

Runnable runnable = new NewState();
Thread t = new Thread(runnable);
t.start();
System.out.println(t.getState());

The above code snippet is most likely to return the output as RUNNABLE.

Note: It is possible that the thread reaches a different state by the time control moves to the print statement, so we may get a different output.


WAITING State

In this state, a thread is waiting for another thread to complete a specific action. According to Java Docs, any thread can enter this state by calling one of the following methods:

  • Object.wait()

  • Thread.join()

  • LockSupport.park()

In the next blog, we will discuss the notify(), notifyAll(), and wait() methods in more detail. For now, let's understand the WAITING state with an example.

Example:

public class WaitingState implements Runnable {
    public static Thread t1;

    public static void main(String[] args) {
        t1 = new Thread(new WaitingState());
        t1.start();
    }

    public void run() {
        Thread t2 = new Thread(new DemoWaitingStateRunnable());
        t2.start();

        try {
            t2.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}

class DemoWaitingStateRunnable implements Runnable {
    public void run() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }

        System.out.println(WaitingState.t1.getState());
    }
}

In this example, when we start thread t1, it creates a new thread t2, starts it, and then waits for t2 to complete its execution. This means t1 will only complete after t2 has finished execution. Since we are printing the state of t1 while t2 is running and t1 is waiting, the expected output is WAITING.


TIMED_WAITING State

This is similar to the WAITING state, but instead of waiting indefinitely, the thread waits for a specified period. According to Java Docs, a thread can enter the TIMED_WAITING state in five ways:

  1. Thread.sleep(long millis)

  2. wait(int timeout) or wait(int timeout, int nanos)

  3. Thread.join(long millis)

  4. LockSupport.parkNanos()

  5. LockSupport.parkUntil()

Example:

public class TimedWaitingState {
    public static void main(String[] args) throws InterruptedException {
        DemoTimeWaitingRunnable runnable = new DemoTimeWaitingRunnable();
        Thread t1 = new Thread(runnable);
        t1.start();

        // Giving enough time for ThreadScheduler to start processing t1
        Thread.sleep(1000);
        System.out.println(t1.getState());
    }
}

class DemoTimeWaitingRunnable implements Runnable {
    @Override
    public void run() {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }
}

In the above code snippet, we are printing the state of thread t1 while it is in the sleep stage for 5 seconds, so the expected output will be TIMED_WAITING.


BLOCKED State

If two threads attempt to execute the same synchronized code block, and one thread is already executing it, the second thread will be blocked until the first thread finishes. That means the second thread cannot access the synchronized block until the first one exits.

Example:

public class BlockedState {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new DemoBlockedRunnable());
        Thread t2 = new Thread(new DemoBlockedRunnable());

        t1.start();
        t2.start();

        Thread.sleep(1000);

        System.out.println(t2.getState());
        System.exit(0);
    }
}

class DemoBlockedRunnable implements Runnable {
    @Override
    public void run() {
        commonResource();
    }

    public static synchronized void commonResource() {
        while (true) {
            // Infinite loop to mimic heavy processing
        }
    }
}

Here, since t1 is already executing the synchronized commonResource() method, t2 will not be able to enter and execute that method until t1 finishes. So the expected output will be BLOCKED.


TERMINATED State

This state indicates that a thread has completed execution or was terminated abnormally.

Example:

public class TerminatedState implements Runnable {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new TerminatedState());
        t1.start();

        // Giving enough time for thread t1 to complete
        Thread.sleep(1000);
        System.out.println(t1.getState());
    }

    @Override
    public void run() {
        // No processing in this block
    }
}

In this example, after calling start(), the Thread.sleep(1000) statement ensures enough time for t1 to complete execution. The expected output will be TERMINATED.


Conclusion

These are all the states of a thread, explained with examples. I hope you have understood all the states well!

0
Subscribe to my newsletter

Read articles from Jayita Saha directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jayita Saha
Jayita Saha