Life Cycle of a Thread in Java

In this article, we shall take an in-depth look at the detailed phases of a thread’s life cycle and examine how threads develop, interact or contribute to its dynamic programming environment. We’ll explore each state’s details, highlighting the importance of good thread management and synchronization from the moment a thread is born to its end or termination.

Different Thread States in Java

New:

The “New” state is the initial phase in the life cycle of a thread within a multithreaded environment. The thread is created, but it hasn’t started to execute its assigned code at the time of this state. The “new” state is the beginning of the thread as it emerges from instantiation and serves as the starting point for the thread’s journey through its various stages. Every time a new thread is created, it is always in a new state.

Example:

class MyThread extends Thread{
    public void run(){}
}
public class Main {
    public static void main(String[] args){
        MyThread t = new MyThread();
        System. out.println("TechVidvan-Thread was in a new state");
    }
}

Output:

TechVidvan-Thread is in a new state

Runnable:

Threads enter the “Runnable” state in several ways:

I. After being created and started using the start() method.

II. After being in the “Blocked” or “Waiting” state and becoming ready to execute again.

III. When an operating system thread scheduler allocates processing time to the thread.

It’s important to note that being in the “Runnable” state does not guarantee immediate execution. The actual execution depends on the scheduler’s decisions, which are influenced by factors like thread priorities and fairness.

Example:

class MyThread extends Thread{
    public void run(){}
}
public class Main {
    public static void main(String[] args){
        MyThread t = new MyThread();
        t.start();
        //getState() is used to get the state of the Thread
        System.out.println("TechVidvan-Thread state:"+Thread.currentThread().getState());
    }
}

Output:

TechVidvan-Thread state: RUNNABLE

Running:

Threads enter the “Running” state from the “Runnable” state when they are selected by the thread scheduler for execution. The scheduler allocates CPU time to threads based on priorities, thread states, and the scheduling algorithm used. Threads may also transition to the “Running” state from the “Blocked” or “Waiting” states when the conditions they were waiting for are satisfied.

While threads are in the “Running” state, they actively contribute to the progress of the program, performing computations, processing data, and executing instructions. Effective synchronization and coordination mechanisms are important during this phase to manage shared resources and ensure proper communication between threads

Example:

class MyThread extends Thread{
    public void run(){
        for(int i=1;i<4;i++){
            System.out.println("TechVidvan-Thread is in Running state");
            String s="TechVidvan-Thread: "+i;
            System.out.println(s);
        }
    }
}
public class Main {
    public static void main(String[] args){
        MyThread t = new MyThread();
        t.start();
    }
}

Output:

TechVidvan-Thread is in a Running state
TechVidvan-Thread : 1

TechVidvan-Thread is in a Running state
TechVidvan-Thread : 2

TechVidvan-Thread is in a Running state
TechVidvan-Thread : 3

Blocked:

Threads enter the “Blocked” state when they need access to a resource that is currently unavailable or when they are waiting for a certain event to occur. This state helps manage thread interactions and resource sharing, preventing multiple threads from accessing or modifying shared resources simultaneously and potentially leading to data inconsistencies or conflicts.

Common scenarios that lead to the “Blocked” state include:

I. Waiting for a Lock: When a thread attempts to acquire a lock on a synchronized block or method that is already held by another thread, it enters the “Blocked” state. The thread will remain blocked until the lock becomes available.

II. I /O Operations: While waiting for an I/O action to finish, threads doing I/O activities, such as reading from or writing to a file or network socket, may enter the “Blocked” state.

III. Waiting for a Condition: Threads waiting for a certain condition to be met, often signalled by another thread, can enter the “Blocked” state until the condition is satisfied.

IV. Explicit Blocking: Threads can be explicitly blocked by calling methods like Thread.sleep() or Object.wait(). These methods suspend the thread’s execution for a specified time or until another thread notifies them.

Example:

class MyThread extends Thread{
    int i=1;
    public void run(){
        while(i<6){
            System.out.println("TechVidvan-Thread "+i);
            //try-catch block to handle exceptions that may be raised when a thread enters a blocked state
            try{
                System.out.println("TechVidvan-Thread enters Blocked state");
                Thread.sleep(2000);
            }
            catch (InterruptedException e){
                System.out.println("InterruptedException has been raised");
            }
            i++;
        }
    }
}
public class Main {
    public static void main(String[] args){
        MyThread t = new MyThread();
        t.start();
    }
}

Output:

TechVidvan-Thread 1
TechVidvan-Thread enters a Blocked state

TechVidvan-Thread 2
TechVidvan-Thread enters a Blocked state

TechVidvan-Thread 3
TechVidvan-Thread enters a Blocked state

TechVidvan-Thread 4
TechVidvan-Thread enters a Blocked state

TechVidvan-Thread 5
TechVidvan-Thread enters a Blocked state

Waiting:

In contrast to the “Blocked” state, where a thread is prevented from executing due to external factors, threads in the “Waiting” state choose to wait for a specific event or signal. This event is typically triggered by another thread, indicating that a certain condition has been satisfied or an action has been completed.

Common scenarios that lead to the “Waiting” state include:

I. Object Waiting: Threads can enter the “Waiting” state by invoking the Object.wait() method, which temporarily suspends their execution until another thread calls Object.notify() or Object.notifyAll() on the same object.

II. Thread Joining: When a thread calls the join() method on another thread, it enters the “Waiting” state until the joined thread completes its execution.

III. Timed Waiting: Threads can enter a timed “Waiting” state by invoking methods like Thread.sleep() or Object.wait(timeout), where they wait for a specified period before resuming execution.

IV. Waiting for I/O: Threads performing I/O operations, such as reading from or writing to a file or network socket, can enter the “Waiting” state while waiting for the I/O operation to complete.

Once the awaited condition is met or the specified period elapses, the thread transitions back to the “Runnable” state, becoming eligible for execution again. The decision to transition from “Waiting” to “Runnable” is typically triggered by explicit signals, timeouts, or the actions of other threads.

Example:

public class Main {
    public static void main(String[] args) throws InterruptedException{
        MyThread t = new MyThread();
        t.start();//starting the thread
        t.join(2000);//calling the join()
        for(int i=0;i<3;i++){
            System.out.println("TechVidvan-Main Thread");
        }
    }
}
class MyThread extends Thread{
    public void run(){
        for(int i=0;i<3;i++){
            System.out.println("TechVidvan-Thread1");
        }
     }
  }

Output:

TechVidvan-Thread1

TechVidvan-Thread1

TechVidvan-Thread1

TechVidvan-Main Thread

TechVidvan-Main Thread

TechVidvan-Main Thread

Terminated:

In the “Terminated” state, a thread has finished executing its designated code and has fulfilled its purpose within the multithreaded application. It is no longer eligible for execution, and its resources, such as memory and system handles, can be released and reclaimed by the operating system.

Threads can reach the “Terminated” state in several ways:

I. Successful Completion: When a thread completes the execution of its run() method or its designated task, it naturally transitions to the “Terminated” state.

II. Uncaught Exception: If an unhandled exception occurs within a thread and propagates to the top of its call stack, the thread terminates abruptly and enters the “Terminated” state.

III. Thread Termination: Threads can be explicitly terminated by calling the Thread. stop() method. However, this method is considered unsafe and deprecated, as it can leave resources in an inconsistent state.

IV. Once a thread is in the “Terminated” state, it cannot return to any other state in the thread life cycle. However, the termination of one thread does not impact the execution of other threads in the application, which can continue running and contribute to the program’s concurrent processing.

Example:

class MyThread extends Thread {
    public void run() {
        for (int i = 1; i < 4; i++) {
            System.out.println("TechVidvan-Thread " + i);
        }
    }
}
public class Main {
    public static void main(String[] args) throws InterruptedException {
        MyThread t = new MyThread();
        t.start();
        // Wait for the thread to complete
        t.join();
        // Now the thread has completed its execution
        System.out.println("when Techvidvan-Thread execution completed: " + t.getState());
    }
}

Output:

TechVidvan-Thread 1

TechVidvan-Thread 2

TechVidvan-Thread 3

When Techvidvan-Thread execution is completed: TERMINATED

 

life cycle of a thread

Conclusion

In the end, the thread life cycle is a fundamental part of multithreaded programming, which offers synchronized sequences of states where threads are gracefully moving through them.

The “New” state in the thread life cycle marks the inception of a thread, representing the moment when a thread is created and allocated memory.

The “Runnable” state is a dynamic phase where threads are poised for execution, eagerly awaiting their turn to actively contribute to the concurrent processing power of a multithreaded application.

In the “Running” state, threads are actively executing their instructions on the CPU.

The “Blocked” state is a vital component of the thread life cycle, allowing threads to pause temporarily while waiting for specific conditions to be met.

The “Waiting” state represents a deliberate pause in a thread’s execution as it waits for a specific event or condition.

The “Terminated” state marks the conclusion of a thread’s journey within a multithreaded application.