Lifecycle and States of a Thread in Java

In Java, a thread has a lifecycle consisting of several states. The lifecycle of a thread begins when it is created and ends when it is terminated. Here are the different states of a thread in Java:

  1. New: When a thread is created, it is in the new state. In this state, the thread is not yet started and has not begun executing.

  2. Runnable: When a thread is started, it enters the runnable state. In this state, the thread is eligible to be executed by the JVM's thread scheduler. However, it may not be currently executing if there are other threads that have higher priority or are currently executing.

  3. Running: When a thread is selected by the thread scheduler to run, it enters the running state. In this state, the thread is actively executing its code.

  4. Blocked: A thread can enter the blocked state when it is waiting for a monitor lock to be released. For example, if a thread tries to enter a synchronized block that is currently being held by another thread, it will enter the blocked state until the lock is released.

  5. Waiting: A thread can enter the waiting state when it is waiting for some external event to occur. For example, if a thread calls the wait() method on an object, it will enter the waiting state until another thread calls the notify() method on that same object.

  6. Timed Waiting: A thread can enter the timed waiting state when it is waiting for some external event to occur, but with a time limit. For example, if a thread calls the sleep() method or the wait(long) method on an object, it will enter the timed waiting state for the specified amount of time.

  7. Terminated: A thread can enter the terminated state when it has finished executing its code or when an exception is thrown that causes it to terminate prematurely.

Here's an example that demonstrates the different states of a thread:

public class MyThread extends Thread {
  public void run() {
    System.out.println("Thread is running");
    try {
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Thread is done");
  }
}
public class Main {
  public static void main(String[] args) {
    MyThread myThread = new MyThread();
    System.out.println("Thread state: " + myThread.getState());
    myThread.start();
    System.out.println("Thread state: " + myThread.getState());
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Thread state: " + myThread.getState());
    try {
      myThread.join();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    System.out.println("Thread state: " + myThread.getState());
  }
}

In this example, we create a new thread myThread and print its state, which is initially NEW. We then start the thread and print its state, which should be RUNNABLE. We wait for a short period of time and print the thread's state again, which may be TIMED_WAITING if the thread has executed the sleep() method. Finally, we wait for the thread to complete using the join() method and print its state, which should be TERMINATED.