Site icon JavaGoal

Thread life cycle in java

In a recent post, we have seen what is the thread in java and thread creation in java. Let’s understand the Thread Life Cycle in Java and Thread State in java. If you want to learn multithreading in java, you must know about the life cycle of threads in os. We will explain the thread life cycle in java with a different example.
Each thread is controlled by the Thread scheduler. A thread can be in any state which are the following:

1. NEW state
2. RUNNABLE state
3. RUNNING state(This state doesn’t exist)
4. TIMED WAITING state
5. WAITING state
6. BLOCKED state
7. TERMINATED state

In this post we will cover some more concepts:
1. Memory representation of thread creation in java?
2. How do multiple threads work?
3. How does thread work if an exception occurs?
4. start() and run() method?
5. How does the start() method work?
6. If we run() method called instead of start() method?
7. What is the main thread in java?
8. Important points about the main thread?

Thread life cycle in java

1. NEW state

It is the first state of the thread after the creation of a thread. When we create a thread object by use of the new operator, the thread enters in NEW state. In this state, the thread is not considered alive. A thread keeps the NEW state until JVM calls the start() method. Once JVM calls the start() method, the thread leaves the NEW state and moves to another state. A thread can’t move back to the NEW state after the call of the start() method.

2. RUNNABLE state

When the JVM invokes the start() method on the thread object, the thread leaves the NEW state and moves to the RUNNABLE state. Meanwhile, the control transfers to the Thread scheduler to finish its execution. The thread scheduler decides whether to run the thread instantly or keep it in a runnable thread pool before running. If the thread is in the RUNNABLE state it means the CPU can run it at any time. When the thread gets a chance to run its states will be changed accordingly. It is the responsibility of the thread scheduler to give the chance to thread.
The thread in a RUNNABLE state is considered to be alive and it can be returned back to this state from the  WAITING, BLOCKED state.

NOTE: In the RUNNABLE state a thread can be either running or ready to run.

In multi-threaded program can have multiple threads.  The thread scheduler allocates a fixed amount of time to each individual thread. Each thread gets CPU for a short-fixed time and then pauses so that other threads can get a chance to run. When the execution of a thread is paused by a thread scheduler, the thread is ready to run, and it is waiting for the CPU. It means the thread lies in a runnable state.

3. RUNNING state

The RUNNING state doesn’t exist in reality, but it’s considered a part of the RUNNABLE state. As we know in a RUNNABLE state, the control transfers to the thread scheduler and the thread scheduler pick one of the thread from the runnable thread pool and change its state to Running. It is the state in which the CPU executes the code of the thread. 

4. TIMED WAITING

A RUNNABLE thread can move to the TIMED WAITING state for a specific time interval. Meantime the CPU picks another thread that is ready to run and waiting for the CPU cycle. When a thread is in the TIMED WAITING state it means that the thread lies in this state for a specified time period. It will remain in this state until the given time interval expires, or a notification is received. Any thread can be put in a TIMED WAITING state when it calls a method with a time-out parameter.
There are some methods that are used to move the thread in the TIMED WAITING state like sleep(time), wait(timeout), join(timeout), parkNanos(), parkUntil().

Let’s understand it with an example. If there are two threads T1 and T2. The T1 thread will take one hour to complete its corresponding job and the T2 thread will take 10 seconds. Let’s say the T1 thread is in the running state and the T2 thread is in the RUNNABLE state(Ready to run and to wait for the CPU cycle). In this scenario, the T2 thread must wait 1 hour to complete the execution of 10 seconds. To execute the T2 thread in between we can call the waiting(timeOut) method and move the T1 thread to the TIMED WAITING state because T2 needs to execute only for 10 seconds on priority. Now T2 will move to the running state and get the CPU.

The thread T1 is in the TIMED WAITING state only for 10 seconds. The T1 thread will be again in the RUNNABLE state only if the given time will be expired or any thread gives the notification. It is the responsibility of the thread scheduler to determine which thread should be run.

5. WAITING state

When a thread is in the WAITING state, it means some other threads are on priority. A thread can be in the state for various reasons. We can put a thread in the WAITING state by calling its wait() method, join() method or park() method.

For example: If there are two threads T1 and T2. T1 will take one hour to complete their corresponding job and T2 will takes 10 seconds. Let’s say T1 is in running state and T2 is in the RUNNABLE state(Ready to run and to wait for the CPU cycle). So, we call the waiting() method and move the T1 thread to the WAITING state, because T2 needs to execute only for 10 seconds on priority. Now T2 moved to the RUNNABLE state and get the CPU. The thread T1 in the WAITING state is scheduled by the thread scheduler. Once the thread wait state is over its state is changed to a runnable state and it’s moved back to the thread pool. It is the responsibility of the thread scheduler to determine which thread to run.

6. BLOCKED state

When a thread is in the BLOCKED state it is not eligible to run. Although the thread is considered to be alive. A RUNNABLE thread can move to the BLOCKED state if a thread wants to perform some operation but can’t complete it immediately so it must temporarily wait until that task completes. After the BLOCKED state, the thread moves to the RUNNABLE state and waits for the CPU cycle.

For example, A thread is waiting for I/O operations, but some other thread already occupies the I/O operations. So now the thread must wait for I/O operation, and it lies in the blocked state. It’s the responsibility of the thread scheduler to reactivate and schedule a blocked thread. A blocked thread can’t be executed further until it is moved to a RUNNABLE state. The threads which are in blocked states do not consume any CPU cycle.

If there are two threads T1 and T2. T1 is in the RUNNABLE state(Ready to run and to wait for the CPU cycle) and T2 is running by CPU. If T2 (running thread) is moved to the blocked state, then another thread(T1) in the runnable state is scheduled by the thread scheduler to run. It is the responsibility of the thread scheduler to determine which thread to run.

7. Terminated state

It is a dead state of the thread. In this state, the thread does not consume any cycles of CPU. A thread terminates because of some reasons:

class MainClass extends Thread
{
  public static Thread thread1; 
  public static Thread thread2; 
	
  public static void main(String arg[])
  {
    thread1 = new MainClass();
	
    // thread1 is just created so now it is in NEW state
    System.out.println("Getting the state of thread1 after creation of thread1 = " + 
    thread1.getState());
				
    thread1.start();
				
    // Start() method moved the thread1 to RUNNABLE state
    System.out.println("Getting the state of thread1 after call start method by thread1 = " 
    + thread1.getState());
   }
   public void run()
   {
     thread2 = new ExampleOfThreadStates();
	 
     // thread2 is just created so now it is in NEW state
     System.out.println("Getting the state of thread2 after creation of thread2 = "+  
     thread2.getState());
					
      thread2.start();
				
      // Start() method moved the thread2 to RUNNABLE state
      System.out.println("Getting the state of thread2 after call start method by thread2 = 
      " + thread2.getState());
		
      try  
      {
	// The sleep method move the thread2 in TIMED_WAITING and thread1 in RUNNABLE. 
        // Because sleep method is used to sleep the current executing thread.
	Thread.sleep(500);
	System.out.println("Getting the state of thread1 after call the sleep method = " + 
        thread1.getState());
	System.out.println("Getting the state of thread2 after call the sleep method = " + 
        thread2.getState());
       } 
       catch (InterruptedException e) 
       {
	  e.printStackTrace();
	}
		
	try 
	{
	  // Now join method is called by use of thread2 it means thread1 can't be execute 
          // until thread2 execute completely.
	  thread2.join();
	} 
	catch (InterruptedException e) 
        {
	   e.printStackTrace();
	}
		
	System.out.println("Getting the state of thread1 after completion of thread2 = "+ 
        thread1.getState());
        System.out.println("Getting the state of thread2 after completion of thread2 = "+ 
        thread2.getState());
      }
}
class ExampleOfThreadStates extends Thread
{  
	public void run()
	{	
		try
		{ 
			Thread.sleep(1500); 
		} 
		catch (InterruptedException e) 
		{ 
			e.printStackTrace(); 
		} 
               System.out.println("Getting the state of thread1 after calling the join by 
               thread2 = "+  MainClass.thread1.getState()); 
               System.out.println("Getting the state of thread2 after calling the join by 
               thread2 = "+  MainClass.thread2.getState()); 
	 }
} 

Output:
Getting the state of thread1 after creation of thread1 = NEW
Getting the state of thread1 after call start method by thread1 = RUNNABLE
Getting the state of thread2 after creation of thread2 = NEW
Getting the state of thread2 after call start method by thread2 = RUNNABLE
Getting the state of thread1 after call the sleep method = RUNNABLE
Getting the state of thread2 after call the sleep method = TIMED_WAITING
Getting the state of thread1 after calling the join by thread2 = WAITING
Getting the state of thread2 after calling the join by thread2 = RUNNABLE
Getting the state of thread1 after completion of thread2 = RUNNABLE
Getting the state of thread2 after completion of thread2 = TERMINATED

In this example we have two classes, one is MainClass and the other is ExampleOfThreadStates.

In MainClass we have created two threads:

  1. Firstly, We are creating a thread in the main method which is thread1. We call thread1.start() and it runs the run() method of MainClass. In the run() method are creating another thread thread2 of      ExampleOfThreadStates.
  2. Now, thread1 and thread2 both are in executing by CPU. Both are sharing the time slot allocated by the CPU.
  3. In the next line, we are calling Thread.sleep() and thread2 moves to the TIMED_WAITING state. Because the sleep() method is used to sleep the currently executed thread.
  4. The thread2 will remain in the TIMED_WAITING state until the timeout is completed. So thread1 continues with the execution.
  5. In the next line, thread2.join() is called. It means thread1 can’t be executed until thread2 is executed completely. Thread1 moves to the WAITING state and now the control flow goes to thread2.
  6. The thread2 completes the execution successfully and moved to the TERMINATED state and the control flow goes to thread1.
  7. The thread1 moves to the RUNNABLE state.
  8. After completing the run() method of MainClass thread1 moves to TERMINATED state.

Memory representation of thread creation in java

Each java application has at least one thread, it can have more than one thread that works concurrently. The JVM allocates a stack to each thread, that is used for storing runtime data. When JVM starts the execution from the main method it creates a stack for the main thread and stores the data in the particular stack.

All the method calls are stored in the stack and each entry is known as a stack frame. A stack can have several stack frames that totally depend upon the stack size. If a thread tries to store more items on the stack than the stack size allows, the thread will throw a stack overflow error.

Let us see how the main thread is created by JVM and how it works.

public class JavaGoal
{
	public static void main(String args[]) 
	{
		System.out.println("Main method");
		JavaGoal obj = new JavaGoal();
		obj.firstMethod();
		obj.secondMethod();
	}
	
	public void firstMethod()
	{
		System.out.println("First Method");
	}
	
	public void secondMethod()
	{
		System.out.println("Second Method");
	}
}

Output: Main method
First Method
Second Method

In the above example, we have the main method and two other methods(firstMethod and secondMethod). The JVM starts the execution from the main method and creates a stack for the main thread. The other methods are getting calls from the main method and created a new stack frame for each method call.

1. JVM creating a stack for the main thread.
2. The main method calls the firstMethod() and the JVM creates an entry as a frame stack.
3. Again, the main method calls the secondMethod() and the JVM creates another frame stack in the stack.

After the execution of each method, its corresponding entry(stack frame) will be removed from the stack. When the stack is empty then the run-time stack is destroyed by the JVM.

How do multiple threads work?

As we know an application can have any number of threads. The JVM always runs the main thread first because it is the entry point of the program. All the other threads must be part of the main thread it means each thread is start execution after the main thread. Suppose we have multiple threads in a program then JVM will create multiple stacks at run time.

1. The JVM creates a runtime stack for each thread.
2. Each thread has a separate stack that stores the data of the particular thread.
3. Each call is stored in the stack as a separate entry known as a stack frame.
4. After completion of each call, the corresponding stack frame is removed from the stack.
5. After completion of all calls, the stack will be empty, and the JVM automatically destroyed the
stack.

class ExampleOfThreadCreation extends Thread
{  
   public static void main(String args[])
   {  
	   firstMethod();
	   SecondMethod();
       ExampleOfThreadCreation t1 = new ExampleOfThreadCreation();
       t1.start();
   }  
   public void run()
   {
	   System.out.println("Thread is running...");
	   for(int i = 0; i < 5 ; i++)
		   methodOfThread(i);
	  
   }
   
   public static void firstMethod()
   {
	   System.out.println("First method");
   }
   
   public static void SecondMethod()
   {
	   System.out.println("Second method");
   }
   
   public static void methodOfThread(int i)
   {
	   System.out.println("Counting in thread : " + i);
   }
}  

Output: First method
Second method
Thread is running…
Counting in thread : 0
Counting in thread : 1
Counting in thread : 2
Counting in thread : 3
Counting in thread : 4

How does thread work if an exception occurs?

The thread does work differently if an exception occurs in the program. The JVM handles it step by step and recovers the exception if the exception handling code is present.

NOTE: If you are not familiar with Exception handling then read it first.

1. Suppose a NullPointerException is occurring in the method, then the JVM checks the exception handling code is there. If code is not presented, then JVM creates an object of exception and removes the current frame from the stack.

2. After that JVM checks the caller method if it is having any exception handling code is not. If code is not presented there, JVM terminates the execution and removes the current frame from the stack.

3. The above process continues until the JVM reaches the main thread. If the main thread doesn’t have an exception handling code, then JVM terminates the execution and prints the exception by use of the default exception handler.

class ExampleOfThreadCreation extends Thread
{  
   public static void main(String args[])
   {  
	   firstMethod();
   }  
  
   public static void firstMethod()
   {
	   System.out.println("First method");
	   SecondMethod();
   }
   
   public static void SecondMethod()
   {
	   System.out.println("Second method");
	   int a = 2/0;
   }
}  

Output: First method
Second method
Exception in thread “main” java.lang.ArithmeticException: / by zero at ExampleOfThreadCreation.SecondMethod(ExampleOfThreadCreation.java:17)  at ExampleOfThreadCreation.firstMethod(ExampleOfThreadCreation.java:11) at ExampleOfThreadCreation.main(ExampleOfThreadCreation.java:5)

1. In the above example, a NullPointerException is occurring at method secondMethod(). The JVM will check whether the exception handling code is presented or not. If not, then JVM moves to the caller method and removes the current stack frame from the stack.

2. In the second step the JVM is checking the exception code in the firstMethod() (the caller method()). If it’s not having an exception handling code, then it removes the current stack frame from the stack.

3. At the last, the JVM checks the main thread(main method) if have any exception handling code is present or not. If not, then terminates the main method abnormally and the default exception handler is responsible to print the exception message to the output screen which is part of JVM.

start() and run() method

As we know, we have two ways to create a thread in java. In both ways, we need to provide the body to the run() method. Actually, the run() method is the body of the thread that executes when JVM calls the start() method. But the question arises why do we call the start() method instead of the run() method?

To find the answer we will look at how does the start() method and run() method work?

How does the start() method work?

As we know whenever a thread executes the JVM creates a new stack and stores the thread data in the stack. If you are not familiar with How to thread creation in memory you should read it first. Here is the link. Whenever the JVM invokes the start() method there are certain operations are performed.

1. After invoking the start() method, JVM will create a run-time stack for the particular thread. All
the method calls of the particular thread enter the stack and know as a stack frame.
2. The method body of the thread will be executed.
3. Value is returned and the current stack frame is popped from the call stack.

NOTE: The start() method creates a separate call stack for each thread. and then run() is called by JVM. The run() popped the current stack frame from the call stack. 

If we run() method called instead of start() method

If we call the run() method instead of the start() method then the program executes as a normal program and it is not considered as a separate thread. The run() method doesn’t create a separate stack for the new thread. The run() method goes onto the current call stack rather than at the beginning of a new call stack. So, this will work as a normal program because each thread needs a separate call.

Let’s see how does program work if we call the start() method:

public class StartAndRunMethod 
{
	public static void main(String arg[])
	{
		ThreadExample thread1 = new ThreadExample();
		thread1.setName("Thread1");
		thread1.start();
		
		ThreadExample thread2 = new ThreadExample();
		thread2.setName("Thread2");
		thread2.start();
	}
}

class ThreadExample extends Thread
{
  public void run()
  {
    for(int i = 1; i <= 5 ; i++)
    {
      System.out.println(Thread.currentThread().getName()+"is running and value of i = "+i);
      try 
      {
	Thread.sleep(1000);
      } 
      catch (InterruptedException e) 
      {
	e.printStackTrace();
      }
    }
  }
}

Output:
Thread1 is running and value of i = 1
Thread2 is running and value of i = 1
Thread1 is running and value of i = 2
Thread2 is running and value of i = 2
Thread2 is running and value of i = 3
Thread1 is running and value of i = 3
Thread1 is running and value of i = 4
Thread2 is running and value of i = 4
Thread1 is running and value of i = 5
Thread2 is running and value of i = 5

In the above example start() method create separate call stack for each thread. So, both are running and printing values.

Now we will see how does program work if we call run() method:

public class StartAndRunMethod 
{
	public static void main(String arg[])
	{
		ThreadExample thread1 = new ThreadExample();
		thread1.setName("Thread1");
		thread1.run();
		
		ThreadExample thread2 = new ThreadExample();
		thread2.setName("Thread2");
		thread2.run();
	}
}

class ThreadExample extends Thread
{
  public void run()
  {
    for(int i = 1; i <= 5 ; i++)
    {
     System.out.println(Thread.currentThread().getName()+"is running and value of i = " +i);
      try 
      {
	 Thread.sleep(1000);
      } 
      catch (InterruptedException e) 
      {
	 e.printStackTrace();
      }
    }
  }
}
main is running and value of i = 1
main is running and value of i = 2
main is running and value of i = 3
main is running and value of i = 4
main is running and value of i = 5
main is running and value of i = 1
main is running and value of i = 2
main is running and value of i = 3
main is running and value of i = 4
main is running and value of i = 5

In the above example run() method doesn’t create a separate call stack for each thread. So, It’s working as a normal program.

What is the main thread in java?

As we know every java program has a main method. The main method is the entry point to execute the program. So, when the JVM starts the execution of a program, it creates a thread to run it and that thread is known as the main thread.
If you are creating a simple program to print “Hello world” it means JVM will create the main thread to execute it. You must saw if you try to run a Java program with compilation errors the JVM shows the error in the main thread.

class ExampleOfThreadCreation
{  
   public static void main(String args[])
   {
	   a;
	   System.out.println("Hello world");
   }  
}  

Output: Exception in thread “main” java.lang.Error: Unresolved compilation problems:     Syntax error, insert “VariableDeclarators” to complete LocalVariableDeclaration a cannot be resolved at ExampleOfThreadCreation.main(ExampleOfThreadCreation.java:5)

The JVM automatically creates the main thread but we can verify it and also control this thread.  To control the main thread, we need to get the reference of the thread. We can get the refence of the main thread by calling the method currentThread( ), which is a public static member of Thread. This method will return the reference of the thread in which it is called. So when we call it in the main thread it will return the reference of the main thread. After that, you can control it just like any other thread. Let’s try it with an example

class ExampleOfThreadCreation
{  
   public static void main(String args[])
   {
	   Thread obj = Thread.currentThread();
	   System.out.println("Name of thread :" +obj.getName());
	   System.out.println("Priority of thread :" +obj.getPriority());
	   // We can set the Name of main thread
	   obj.setName("Main thread");
	   System.out.println("Name of thread :" +obj.getName());
	   System.out.println("Hello world");
   }  
}  

Output: Name of thread :main
Priority of thread :5
Name of thread :Main thread
Hello world

In the above example, we are getting the object by use of the currentThread() method. We are getting the properties of the main thread and also changing the name of the main thread.  

Important points about the main thread

1. Automatically created by JVM

The JVM creates the main thread automatically when it starts the execution of the program. Whether you create any thread or not, the main thread exists in the program. The JVM allocates a stack to each thread, that is used for storing runtime data. When JVM starts the execution from the main method it creates a stack for the main thread and stores the data in the particular stack.
You can read it in detail.

All the method calls store in the stack and each entry is known as a stack frame. A stack can have several stack frames that totally depend upon the stack size. If a thread tries to store more items on the stack than the stack size allows, the thread will throw a stack overflow error.
Let us see how the main thread is created by JVM and how does it work.

public class JavaGoal
{
    public static void main(String args[]) 
    {
        System.out.println("Main method");
        JavaGoal obj = new JavaGoal();
        obj.firstMethod();
        obj.secondMethod();
    }
    
    public void firstMethod()
    {
        System.out.println("First Method");
    }
    
    public void secondMethod()
    {
        System.out.println("Second Method");
    }
}

Output: Main method
First Method
Second Method

2. Produce other threads

It is the thread from which other child threads will be spawned. Because it is already created by JVM so whenever we will create any thread it is considered as a child of the main thread. As we know default priority of main thread is 5 so all the child thread inherits its priority also.

3. Must be the last thread to finish execution

The main thread must be the last thread to finish the execution because it performs various shutdown options. When the main thread stops, the program stops running.

4. To control the main thread in java

When our program is started then the main thread is created automatically. You can control the main thread by reference of main thread. You can obtain the reference by calling the method currentThread( ) which is present in Thread class.

public class MainThread extends Thread 
{
   public static void main(String[] args) 
   { 
 	// getting reference of Main thread 
	Thread mainThread = Thread.currentThread(); 
		
	// getting name of Main thread 
	System.out.println("The name Current thread = " + mainThread.getName()); 
		
	// changing the name of Main thread 
	mainThread.setName("JAVAGOAL"); 
	System.out.println("After name change of current thread = " + mainThread.getName()); 
			
	// getting priority of Main thread 
	System.out.println("Main thread priority = "+ mainThread.getPriority()); 
			
	// setting priority of Main thread. Priority can be set MIN(1) to MAX(10) 
	mainThread.setPriority(MAX_PRIORITY); 
		
	// Getting the priority after setting up 
	System.out.println("Main thread new priority = "+ mainThread.getPriority()); 
			
	// Main thread creating a child thread 
	ChildThread childThread = new ChildThread(); 
				
	// starting child thread 
	childThread.start(); 
    } 
} 

// Child Thread class 
class ChildThread extends Thread 
{ 
   @Override
   public void run() 
   { 
	// It returns child thread because after calling of childThread.start() 
        //current thread is child thread.
	ChildThread childThread = (ChildThread) Thread.currentThread();
	// getting name of Main thread 
	System.out.println("The name child thread = " + childThread.getName()); 
					
	// changing the name of child thread 
	childThread.setName("ChildThreadOfJAVAGOAL"); 
	System.out.println("After name change of child thread = " + childThread.getName()); 
						
	// getting priority of child thread 
	System.out.println("Child thread priority = "+ childThread.getPriority()); 
					
	// setting priority of child thread.
	childThread.setPriority(MIN_PRIORITY); 
		
	// Getting the priority after setting up 
	System.out.println("Main thread new priority = "+ childThread.getPriority()); 
			
	System.out.println("This is child thread");
      } 
} 

Output: The name Current thread = main
After name change of current thread = JAVAGOAL
Main thread priority = 5
Main thread new priority = 10
The name child thread = Thread-0
After name change of child thread = ChildThreadOfJAVAGOAL
Child thread priority = 10
Main thread new priority = 1
This is child thread

In the above example, we are getting the main thread by calling Thread.currentThread().  We can set the name and priority of the main thread. We are creating another thread from the main thread that child thread.

5. Creating a deadlock by the main thread in java

As you know main thread should be terminated at last when a number of threads are running. If you call join method by the main thread the program goes to deadlock. For more details, you should read the join method.

public class MainThreadDeadlock 
{
  public static void main(String[] args) 
  {
     try 
     {
	Thread.currentThread().join();
        // the following statement will never execute because main thread join itself 
        System.out.println("This statement will never execute"); 
     }
     catch (InterruptedException e) 
     {
	e.printStackTrace();
     }
  }
}
Exit mobile version