Table of Contents
Java threads run on the JVM (Java Virtual Machine). A thread methods in java is an execution context that can simultaneously execute different tasks, but only one task at a time. While the GIL (Global Interpreter Lock) ensures that only one thread runs at a time, threads still perform better than processes with real parallelism and real concurrency because multiple threads can be running at the same time in different parts of the code. To write efficient multithreaded code, it’s important to understand what’s going on inside your Thread objects as they’re executed on the JVM. A thread, sometimes called a lightweight process, is the smallest unit of execution in Java and most other operating systems. It represents an execution flow of instructions performed by the computer’s processor(s).
Get the latest updates on java programming in the Entri app
Threads are the execution context of actions that aren’t related to each other but must be performed at the same time. For example, two threads might run in parallel in order to calculate values from two different sets of data faster than running them sequentially; another thread method in java might need to wait until the results of one thread are finished before it can continue its own calculations. A thread in Java has a finite life cycle, with different states it goes through during its lifetime, each requiring different actions to be taken by the programmer. Following is an overview of the life cycle of a thread in Java. As you read on, you’ll understand why and when certain steps are executed, and how they affect the behavior of your program.
To know more about java programming in the Entri app
What are the Different States of a Thread?
A thread in Java can exist in any one of the following states at any given moment
- New
- Active
- Blocked / Waiting
- Timed Waiting
- Terminated
1)New:
Whenever a new thread is created, it is always in the initial state. For a thread in the initial state, the code has not been executed yet and thus has not begun its execution.
2)Active:
When a thread invokes the start() method, it moves from the new state to the active state. The active state includes two states; one is runnable, and the other is running.
Runnable:
A thread moves from the waiting state to a runnable state when it has been set up and has become ready to execute. A thread will stay in this state until another one needs its time slot, at which point it will transition back to the waiting stage. Threads are also prioritized according to certain criteria – the priority determines what order they’ll be executed in relative to each other; higher-priority threads will always get precedence over lower-priority ones if two threads are simultaneously scheduled for execution. A process using multi-threading shares a predetermined amount of time to each thread. Every thread is assigned a set amount of seconds during which it runs until its allotted timeframe has elapsed and then willingly yields control back to another thread. When this happens, all threads that wish to run but are not currently running wait in an ordered line inside the code called runnable. Once they are given permission, these threads take their turn completing the necessary function until they give up control again.
Running:
When the thread gets the CPU, it moves from the runnable to the running state. Generally, the most common change in the state of a thread is from runnable to running and again back to runnable.
3)Blocked or Waiting:
Whenever a thread is inactive for a span of time (not permanently) then, either the thread is in the blocked state or is in the waiting state. When one thread (call it Thread A) needs to print something, but another thread is already printing something on the printer at the same time, then Thread A has to wait for Thread B before doing anything. So when Thread A is blocked, it cannot execute any code and so does not take up any CPU cycles. This means that while Thread A might be sitting there waiting, it isn’t consuming any resources. When the scheduler wakes up whatever was in a waiting state (such as Thread A), then things are back to normal again.
When the main thread invokes the Join() method then, it is said that the Main Thread is in the Waiting State. The Main Thread then waits for all of its Child Threads to complete their tasks. Once all of its Child Threads are done with their task, a Notification will be sent to the Main Thread and it will move back into Active Status. If there are a lot of threads in the waiting or blocked state, then it is the duty of the thread scheduler to determine which thread to choose and which one to reject, and the chosen thread is then given the opportunity to run.
4)Timed Waiting:
Waiting for someone or something can often be costly. For instance, if Thread A enters a critical section of code and refuses to leave, another thread- say Thread B- will have to wait indefinitely until it is granted access. This could lead to starvation if not handled properly; luckily there are solutions against this problem. One solution is the use of timers that put Thread B in standby mode until timeout expires; this would allow other threads to move forward while still maintaining balance with Thread A. If you take too long on one task then there’s no telling what might happen – you never know who needs you or where the next opportunity will arise! So always keep your eyes open and your ears peeled just in case someone needs some help or wants advice!
5)Terminated:
A thread can terminate due to the following reasons:
When a thread has finished its job, then it either exists or terminates normally. Abnormal termination: It occurs when some unusual events such as an unhandled exception or segmentation fault happen. A terminated thread means the thread is no more in the system – which essentially means that the thread is dead and there’s nothing we can do about it except hope for it to respawn if active before being killed off.
A life cycle of a thread in java is considered running if it is either on CPU or pre-empted by another thread. Once a thread has been interrupted by another thread, it becomes runnable. In many instances, threads can become blocked as they wait for data from I/O operations like network access or disk I/O. In these situations, they have no choice but to wait until such time that there’s no longer an issue with the I/O operation and then proceed with their processing. Occasionally, threads become blocked because their task has yet to be completed. This means that once a thread has been created for performing specific tasks or purposes that it will only continue its execution once those tasks have been completed.
Get the latest updates on java programming in the Entri app
The reason why this happens is that the OS allocates CPU resources to threads that are currently executing while simultaneously suspending other ones. When allocating CPU resources, most operating systems use some form of the scheduling algorithm. The two primary forms of scheduling algorithms used today are preemptive and cooperative multitasking. Preemptive multitasking ensures that one process isn’t allowed to monopolize CPU resources without interruption while cooperative multitasking requires one process voluntarily yields when others need more CPU cycles.
Java Program for the Demonstrating Thread States
The below-mentioned Java program shows some of the states of a thread defined above.
FileName: ThreadState.java
// ABC class implements the interface Runnable
class ABC implements Runnable
{
public void run()
{
// try-catch block
try
{
// moving thread t2 to the state timed waiting
Thread.sleep(100);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
System.out.println(“The state of thread t1 while it invoked the method join() on thread t2 -“+ ThreadState.t1.getState());
// try-catch block
try
{
Thread.sleep(200);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
}
}
// ThreadState class implements the interface Runnable
public class ThreadState implements Runnable
{
public static Thread t1;
public static ThreadState obj;
// main method
public static void main(String argvs[])
{
// creating an object of the class ThreadState
obj = new ThreadState();
t1 = new Thread(obj);
// thread t1 is spawned
// The thread t1 is currently in the NEW state.
System.out.println(“The state of thread t1 after spawning it – “ + t1.getState());
// invoking the start() method on
// the thread t1
t1.start();
// thread t1 is moved to the Runnable state
System.out.println(“The state of thread t1 after invoking the method start() on it – “ + t1.getState());
}
public void run()
{
ABC myObj = new ABC();
Thread t2 = new Thread(myObj);
// thread t2 is created and is currently in the NEW state.
System.out.println(“The state of thread t2 after spawning it – “+ t2.getState());
t2.start();
// thread t2 is moved to the runnable state
System.out.println(“the state of thread t2 after calling the method start() on it – “ + t2.getState());
// try-catch block for the smooth flow of the program
try
{
// moving the thread t1 to the state timed waiting
Thread.sleep(200);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
System.out.println(“The state of thread t2 after invoking the method sleep() on it – “+ t2.getState() );
// try-catch block for the smooth flow of the program
try
{
// waiting for thread t2 to complete its execution
t2.join();
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
System.out.println(“The state of thread t2 when it has completed it’s execution – “ + t2.getState());
}
}
Output:
The state of thread t1 after spawning it - NEW The state of thread t1 after invoking the method start() on it - RUNNABLE The state of thread t2 after spawning it - NEW the state of thread t2 after calling the method start() on it - RUNNABLE The state of thread t1 while it invoked the method join() on thread t2 -TIMED_WAITING The state of thread t2 after invoking the method sleep() on it - TIMED_WAITING The state of thread t2 when it has completed it's execution - TERMINATED
Conclusion
In reality, it may be hard to keep all these steps in mind when you’re coding. I usually try to write a small test case and just practice writing some different examples. There are many good sources that dive into each step much deeper (such as below), but without first working with these examples, it might be too abstract to understand what’s going on. The key is being able to see an example run and then using that as inspiration for your own code. Eventually, you’ll get used to thinking about threads and concurrency issues and will start noticing when there might be an issue with your code or design. If you are interested to learn new coding skills, the Entri app will help you to acquire them very easily. Entri app is following a structural study plan so that the students can learn very easily. If you don’t have a coding background, it won’t be a problem. You can download the Entri app from the google play store and enroll in your favorite course.
Enroll in our latest java programming course in the Entri app