Updates
  • Starting New Weekday Batch for Java Full Stack Developer on 24th June 2024 @ 02:00 PM to 04:00 PM
  • Starting New Weekend Batch for Java Full Stack Developer on 06th July 2024 @ 11:00 AM to 02:00 PM
  • Starting New Weekday Batch for Java Full Stack Developer on 08th July 2024 @ 04:00 PM to 06:00 PM
  • Starting New Weekday Batch for Java Full Stack Developer on 29th July 2024 @ 10:00 AM to 12:00 PM
Join Course

Thread Life-Cycle

As we know, each thread is processed by the life cycle mechanism, and in order to complete its processing, it has to go through different states.

Thread Life-Cycle States: The following are the states of the Thread Life-Cycle.

1. Ready To Run
2. Running State
3. Sleep State
4. Wait State
5. Dead State
6. Blocked State

1. Ready To Run: In the java.lang.Thread class, there is a method start(). When we invoke the start() method with an object of the thread class, the control moves to the Ready-To-Run state, and it waits for its turn to receive CPU time.
CPU Scheduling algorithm: Always CPU ready with a scheduling algorithm which main task is to keep busy the Running State. Running state should not be idle; CPU used different types scheduling algorithms such as:

a. Priority Based Algorithm
b. First Come First Serve (FCFS)
c. Shortest-Job-First (SJF) Scheduling
d. Shortest Remaining Time
e. Round Robin Scheduling

However, we cannot be certain about which scheduling algorithm is currently in use.

2. Running State: Once a thread moves into the Ready-to-Run state, it waits for its CPU time. When a thread gets its CPU time, the execution depends on the current CPU scheduling algorithm. After obtaining CPU time, the thread moves to the Running state, and the run() method is executed.

            
class Thread1 extends Thread{
	public void run(){
		System.out.println("run method is processing...!");
	}
}
class JTC{
	public static void main(String arg[]){
		Thread1 t1 = new Thread1();
		t1.start();
	}
}
    run method is processing...!
            

As discussed, when we invoke start() with a thread, it moves to the Ready-To-Run state.
t1.start();

Subsequently, t1 acquires CPU time, transitions to the Running state, and executes the run() method.
CPU scheduling algorithms often change. When the scheduling algorithm is altered, the currently running thread shifts from the Running state to the Ready-To-Run state, awaiting its next turn for CPU time. According to the current scheduling algorithm, a thread moves from the Ready-To-Run state to the Running state.

3. Sleep State: When a thread is in the Running state, invoking the sleep(time) method causes the current running thread to transition into the sleep state for the specified period. After completing the allocated sleep time, it moves back to the Ready-To-Run state, awaiting its next turn for CPU time. During the sleep state, the thread cannot be interrupted.

There are 2 overloaded sleep methods in java.lang.Thread class:

a. public static void sleep(long) throws java.lang.InterruptedException.

Access Modifier: - public
Member Type: - Static Method
Return Type: - void
Method Name: - sleep
Method Parameter: - long
Functionality: - When we invoke this method, the current running thread moves into a sleep state for a specified time period, which we specify at the time of the sleep method invocation as an argument. As we can see, the sleep method takes a long type parameter, so during its invocation, we must pass a long type argument, representing the time in milliseconds.

b. public static void sleep(long, int) throws java.lang.InterruptedException.

Access Modifier: - public
Member Type: - static Method
Return Type: - void
Method Name: - sleep
Method parameter: - long, int
Functionality: - When we invoke this method, the current running thread moves into a sleeping state for a certain time period, which we specify at the time of the sleep method invocation as arguments. As we can see, the sleep method has both long and int types of parameters. Therefore, during its invocation, we must pass a long type argument representing time in milliseconds and an int type argument representing time in nanoseconds.

            
class Thread1 extends Thread{
	public void run(){
		for(int i = 1; i <= 25; i++){
			System.out.println("Current Thread :- "+Thread.currentThread().getName()+" | i :- "+i);
			try{
				Thread.sleep(500);
			}catch(java.lang.InterruptedException e){
				e.printStackTrace();
			}
		}
	}
}
class JTC{
	public static void main(String arg[]){
		Thread1 t1 = new Thread1();
		t1.setName("JTC-THREAD-1");
		t1.start();
	}
}
    Current Thread :- JTC-THREAD-1 | i :- 1
    Current Thread :- JTC-THREAD-1 | i :- 2
    Current Thread :- JTC-THREAD-1 | i :- 3
    Current Thread :- JTC-THREAD-1 | i :- 4
    Current Thread :- JTC-THREAD-1 | i :- 5
    Current Thread :- JTC-THREAD-1 | i :- 6
    Current Thread :- JTC-THREAD-1 | i :- 7
    Current Thread :- JTC-THREAD-1 | i :- 8
    Current Thread :- JTC-THREAD-1 | i :- 9
    Current Thread :- JTC-THREAD-1 | i :- 10
    Current Thread :- JTC-THREAD-1 | i :- 11
    Current Thread :- JTC-THREAD-1 | i :- 12
    Current Thread :- JTC-THREAD-1 | i :- 13
    Current Thread :- JTC-THREAD-1 | i :- 14
    Current Thread :- JTC-THREAD-1 | i :- 15
    Current Thread :- JTC-THREAD-1 | i :- 16
    Current Thread :- JTC-THREAD-1 | i :- 17
    Current Thread :- JTC-THREAD-1 | i :- 18
    Current Thread :- JTC-THREAD-1 | i :- 19
    Current Thread :- JTC-THREAD-1 | i :- 20
    Current Thread :- JTC-THREAD-1 | i :- 21
    Current Thread :- JTC-THREAD-1 | i :- 22
    Current Thread :- JTC-THREAD-1 | i :- 23
    Current Thread :- JTC-THREAD-1 | i :- 24
    Current Thread :- JTC-THREAD-1 | i :- 25
            

4. Wait State: In java.lang.Object class we have 3 overloaded wait methods. When we invoke the wait method then current running thread moves to waiting state and once specified time duration is completed then it moves to ready to run state and wait for their CPU time.

a. public final native void wait(long) throws java.lang.InterruptedException;

Access Modifier: - public
Member Type: - Instance Method
Return Type: - void
Method Name: - wait
Method Parameter: - long
Functionality: - When we invoke the wait(long) method, the current thread transitions to the wait state for a specified amount of time (in milliseconds), which we pass as a long time argument.

b. public final void wait(long, int) throws java.lang.InterruptedException;

Access Modifier: - public
Member Type: - Instance Method
Return Type: - void
Method Name: - wait
Method parameter: - long, int
Functionality: - When we invoke the wait(long, int) method, the current thread transitions to the wait state for a specified amount of time (in milliseconds, represented by a long type argument, and in nanoseconds, represented by an int type argument).

c. public final void wait() throws java.lang.InterruptedException;

Access Modifier: - public
Member Type: - Instance Method
Return Type: - void
Method Name: - wait
Method parameter: - No
Functionality: - When we invoke the wait() method, the current thread moves to the wait state for an unspecified amount of time. It does not come out of the wait state until we call the notify() or notifyAll() methods.

We have 2 more method in java.lang.Object class which are associated with waiting state:

a. public final native void notify();

Access Modifier: - public
Member Type: - Instance Method
Return Type: - void
Method Name: - notify
Method parameter: - No
Functionality: - When we invoke the notify() method, a thread that has spent the longest time in the wait state moves from the wait state to the Ready-to-Run state.

b. public final native void notifyAll();

Access Modifier: - public
Member Type: - Instance Method
Return Type: - void
Method Name: - notifyAll
Method parameter: - No
Functionality: - When we invoke the notifyAll() method, all threads come out from the wait state and move to the Ready-to-Run state in a specific order, with the thread that has been waiting the longest moving first.

The basis different between sleep statement and wait state are:

a. We can’t interrupt a thread which it is in sleep state but in wait state we can interrupt a thread using notify and notifyAll methods.
b. A thread always goes into the sleep statement for specific amount of time period but we can transfer a thread into wait state for unspecified amount of time.
c. When we a thread move into the sleep it doesn’t release the object lock but in wait state it release the object lock first then moves into the waiting state. [We will look about the Object Lock into the further tutorial.]

            
class A{
	synchronized void m1(){
		for(int i = 1; i <= 20; i++){
			System.out.println("Thread is :- "+Thread.currentThread().getName()+" ---> "+i);
			try{
				this.wait();
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}
class Thread1 extends Thread{	
	A a1;
	Thread1(A a1){
		this.a1 = a1;
	}	
	public void run(){
		a1.m1();
	}
}
class JTC{
	public static void main(String arg[]){
		A a1 = new A();
		Thread1 t1 = new Thread1(a1);
		t1.start();
	}
}
    Thread is :- Thread-0 ---> 1
            

In the above example, as we can see, we sent the current thread into the wait state for an unspecified amount of time by invoking the wait() method. In the output, we can observe that the thread is in the wait state, and the JVM is not terminated.

            
class A{
	synchronized void m1(){
		for(int i = 1; i <= 20; i++){
			System.out.println("Thread is :- "+Thread.currentThread().getName()+" ---> "+i);
			try{
				this.wait();
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
	synchronized void m2(){
		for(int i = 1; i <= 20; i++){
			System.out.println("Thread is :- "+Thread.currentThread().getName()+" ---> "+i);
			try{
				if(i == 15){
					this.notify();
				}
				this.wait(500);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}
class Thread1 extends Thread{	
	A a1;
	Thread1(A a1){
		this.a1 = a1;
	}	
	public void run(){
		a1.m1();
	}
}
class Thread2 extends Thread{
	A a1;
	Thread2(A a1){
		this.a1 = a1;
	}
	public void run(){
		a1.m2();
	}
}
class JTC{
	public static void main(String arg[]){
		A a1 = new A();
		Thread1 t1 = new Thread1(a1);
		Thread2 t2 = new Thread2(a1);
		t1.start();
		t2.start();
	}
}
    Thread is :- Thread-0 ---> 1
    Thread is :- Thread-1 ---> 1
    Thread is :- Thread-1 ---> 2
    Thread is :- Thread-1 ---> 3
    Thread is :- Thread-1 ---> 4
    Thread is :- Thread-1 ---> 5
    Thread is :- Thread-1 ---> 6
    Thread is :- Thread-1 ---> 7
    Thread is :- Thread-1 ---> 8
    Thread is :- Thread-1 ---> 9
    Thread is :- Thread-1 ---> 10
    Thread is :- Thread-1 ---> 11
    Thread is :- Thread-1 ---> 12
    Thread is :- Thread-1 ---> 13
    Thread is :- Thread-1 ---> 14
    Thread is :- Thread-1 ---> 15
    Thread is :- Thread-0 ---> 2
    Thread is :- Thread-1 ---> 16
    Thread is :- Thread-1 ---> 17
    Thread is :- Thread-1 ---> 18
    Thread is :- Thread-1 ---> 19
    Thread is :- Thread-1 ---> 20
            

In the above example, as we can see, Thread1 goes to the wait state for an unspecified amount of time by invoking the wait() method. In the output, Thread-1 gets CPU time first, then it goes to the wait state. Subsequently, Thread2 gets CPU time and enters the Running state, resulting in the output of the for loop. When the value of 'i' becomes 15, the notify() method is executed. As a result, Thread-1, which was in the wait state, moves to the Ready-to-Run state, acquires CPU time (as observed in the output), and then goes to the wait state again, as per the implementation of its run() method.

[In output Thread-0 is Thread1 and Thread-1 is Thread2 as per the java.lang.Thread class implementation]

            
class A{
	synchronized void m1(){
		for(int i = 1; i <= 10; i++){
			System.out.println("Thread is :- "+Thread.currentThread().getName()+" ---> "+i);
			try{
				this.wait(5000);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
	synchronized void m2(){
		for(int i = 1; i <= 20; i++){
			System.out.println("Thread is :- "+Thread.currentThread().getName()+" ---> "+i);
			try{
				if(i == 15){
					this.notify();
				}
				this.wait(1500);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}
class Thread1 extends Thread{	
	A a1;
	Thread1(A a1){
		this.a1 = a1;
	}	
	public void run(){
		a1.m1();
	}
}
class Thread2 extends Thread{
	A a1;
	Thread2(A a1){
		this.a1 = a1;
	}
	public void run(){
		a1.m2();
	}
}
class JTC{
	public static void main(String arg[]){
		A a1 = new A();
		Thread1 t1 = new Thread1(a1);
		Thread2 t2 = new Thread2(a1);
		t1.start();
		t2.start();
	}
}
    Thread is :- Thread-0 ---> 1
    Thread is :- Thread-1 ---> 1
    Thread is :- Thread-1 ---> 2
    Thread is :- Thread-1 ---> 3
    Thread is :- Thread-1 ---> 4
    Thread is :- Thread-0 ---> 2
    Thread is :- Thread-1 ---> 5
    Thread is :- Thread-1 ---> 6
    Thread is :- Thread-1 ---> 7
    Thread is :- Thread-0 ---> 3
    Thread is :- Thread-1 ---> 8
    Thread is :- Thread-1 ---> 9
    Thread is :- Thread-1 ---> 10
    Thread is :- Thread-0 ---> 4
    Thread is :- Thread-1 ---> 11
    Thread is :- Thread-1 ---> 12
    Thread is :- Thread-1 ---> 13
    Thread is :- Thread-1 ---> 14
    Thread is :- Thread-0 ---> 5
    Thread is :- Thread-1 ---> 15
    Thread is :- Thread-0 ---> 6
    Thread is :- Thread-1 ---> 16
    Thread is :- Thread-1 ---> 17
    Thread is :- Thread-1 ---> 18
    Thread is :- Thread-0 ---> 7
    Thread is :- Thread-1 ---> 19
    Thread is :- Thread-1 ---> 20
    Thread is :- Thread-0 ---> 8
    Thread is :- Thread-0 ---> 9
    Thread is :- Thread-0 ---> 10
            

In this example, we can observe that initially, Thread1 acquires CPU time and then moves to the waiting state by invoking wait(5000), indicating that it will come to the Ready-to-Run state after expending 5000 milliseconds. Meanwhile, Thread2 is executing and also enters the wait state by invoking wait(1500). This is the reason we observe the long execution of Thread2 in between every execution of Thread1.
However, when the value of 'i' becomes 15 in the Thread2 run method implementation, the notify() method is executed. Consequently, Thread1 immediately comes out of the wait state, and we can notice in the output (highlighted in yellow) that just after 'i = 15' in the Thread2 implementation, Thread1 becomes active.

[In output Thread-0 is Thread1 and Thread-1 is Thread2 as per the java.lang.Thread class implementation]

5. Block State: When a thread is transitioning to the Running state to process a task or requesting a specific resource that is currently unavailable, the thread moves to the block state until the requested resource becomes available. The thread in the block state only returns to the Ready-to-Run state when the requested resource becomes available. In such cases, deadlock conditions may arise.

6. Dead State: As we know, we create a thread to perform a task. When the assigned task is completed, the thread automatically moves to the dead state. We can also explicitly transition a thread to the dead state by invoking the stop() method of the java.lang.Thread class. Once a thread is in the dead state, it cannot be restarted or moved back to the Ready-to-Run state.

            
class A{
	synchronized void m1(){
		for(int i = 1; i <= 5; i++){
			try{
				System.out.println(Thread.currentThread().getName()+" ----> "+i);
				this.wait(500);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
	synchronized void m2(){
		for(int i = 1; i <= 5; i++){
			try{
				System.out.println(Thread.currentThread().getName()+" ----> "+i);
				this.wait(500);
			}catch(Exception e){
				e.printStackTrace();
			}
		}
	}
}
class Thread1 extends Thread{
	A a1 = null;
	Thread1(A a1){
		this.a1 = a1;
	}
	public void run(){
		a1.m1();
		this.stop();
		System.out.println(Thread.currentThread().getName()+" is completed...");
	}
}
class Thread2 extends Thread{
	A a1 = null;
	Thread2(A a1){
		this.a1=a1;
	}
	public void run(){
		a1.m2();
		System.out.println(Thread.currentThread().getName()+" is completed...");
	}

}
class JTC{
	public static void main(String arg[]){
		A a1 = new A();
		Thread1 t1 = new Thread1(a1);
		Thread2 t2 = new Thread2(a1);
		t1.start();
		t2.start();
	}
}
    Thread-0 ----> 1
    Thread-1 ----> 1
    Thread-1 ----> 2
    Thread-0 ----> 2
    Thread-0 ----> 3
    Thread-1 ----> 3
    Thread-1 ----> 4
    Thread-0 ----> 4
    Thread-0 ----> 5
    Thread-1 ----> 5
    Thread-1 is completed...
            

In this example, we observe that we send Thread1 to the dead state by invoking the stop() method of the java.lang.Thread class. The effect of this can be seen in the output screen, where after the completion of Thread1, we do not receive the ending message (Thread-0 is completed...) because we called the stop() method just before the ending statement.
On the other hand, in the Thread2 run(), as we can see, we have not made a call to the stop() method. Consequently, we get the closing statement of Thread2 in the output, which is Thread-1 is completed....