Java is a multithreaded programming language, meaning it allows the concurrent execution of two or more threads. Understanding how threads work in Java is essential for writing efficient, scalable, and responsive applications. One of the most important aspects of thread management in Java is the Thread States. In this article, we’ll explore the different states that a thread can go through during its lifecycle, as well as how they interact with one another.
When you create a thread in Java, it undergoes various states during its execution, from being created to being completed. These states determine the behavior and lifecycle of the thread. Java defines six main thread states that are part of the Thread Lifecycle.
The Six Main Thread States in Java
- New (Born) – A thread is in the “New” state after it has been created but before it has started running.
- Runnable – The thread is ready to run and is in the runnable state. However, it is not yet running and waiting for CPU time.
- Blocked – A thread enters the blocked state when it’s waiting to acquire a lock or resource, making it unable to proceed.
- Waiting – A thread in the “Waiting” state is waiting indefinitely for another thread to perform a particular action before it can continue.
- Timed Waiting – A thread is in the timed waiting state when it is waiting for a specific period of time before it can continue.
- Terminated (Dead) – A thread enters the terminated state once its execution completes or if it is forcefully terminated.
Now let’s delve deeper into each of these states and explore how they function with some example code to demonstrate each state in action.
1. New (Born) State
When a thread is created, it’s in the New state. In this state, the thread is not yet started, and it’s only been instantiated. The thread is just an object at this stage and hasn’t begun its execution yet.
class MyThread extends Thread { public void run() { System.out.println("Thread is running."); } } public class TestThread { public static void main(String[] args) { MyThread thread = new MyThread(); // Thread is in 'New' state. } }
Here, when the thread object is created, it is in the New state. The thread’s run()
method has not been invoked yet.
2. Runnable State
Once the start()
method is invoked on a thread, it moves to the Runnable state. In this state, the thread is ready to run and is waiting for the CPU to allocate time for its execution. It’s important to note that a thread in this state might not be executing right away due to CPU scheduling.
class MyThread extends Thread { public void run() { System.out.println("Thread is running."); } } public class TestThread { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // Thread is now in 'Runnable' state. } }
In this example, after calling thread.start()
, the thread enters the Runnable state and waits for the CPU to schedule it for execution.
3. Blocked State
A thread enters the Blocked state when it is trying to acquire a lock or resource that is currently held by another thread. For example, if two threads attempt to access the same synchronized method, the second thread will be blocked until the first thread releases the lock.
class MyThread extends Thread { synchronized public void run() { try { System.out.println("Thread started: " + Thread.currentThread().getName()); Thread.sleep(1000); // Simulating work. } catch (InterruptedException e) { e.printStackTrace(); } } } public class TestThread { public static void main(String[] args) { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); thread1.start(); thread2.start(); // thread2 will be in 'Blocked' state if thread1 is holding the lock. } }
Here, the second thread will be blocked while it waits for the first thread to release the lock on the synchronized method.
4. Waiting State
A thread enters the Waiting state when it is waiting for another thread to perform a specific action. For example, you might want one thread to wait for another to complete its task before it proceeds.
class MyThread extends Thread { public void run() { synchronized(this) { try { wait(); // Thread is in 'Waiting' state. } catch (InterruptedException e) { e.printStackTrace(); } } } } public class TestThread { public static void main(String[] args) throws InterruptedException { MyThread thread = new MyThread(); thread.start(); Thread.sleep(1000); synchronized(thread) { thread.notify(); // Notifies the waiting thread to continue. } } }
In this case, the thread is in the Waiting state until another thread calls notify()
on it.
5. Timed Waiting
The Timed Waiting state occurs when a thread is waiting for a specific period of time to elapse before it can continue. This can happen through methods like sleep()
or join()
with a timeout.
class MyThread extends Thread { public void run() { try { Thread.sleep(2000); // Timed Waiting for 2 seconds. System.out.println("Thread finished sleeping."); } catch (InterruptedException e) { e.printStackTrace(); } } } public class TestThread { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // Thread enters 'Timed Waiting' state. } }
In this example, the thread is in the Timed Waiting state for 2 seconds due to the Thread.sleep(2000)
method.
6. Terminated (Dead) State
A thread enters the Terminated state when its execution is completed or if it is interrupted or terminated prematurely. Once a thread is terminated, it cannot be restarted.
class MyThread extends Thread { public void run() { System.out.println("Thread is running."); } } public class TestThread { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // Thread is in 'Runnable' state and will eventually be in 'Dead' state when execution completes. } }
In this case, the thread completes its execution and enters the Dead state.
In conclusion, understanding the different thread states in Java is crucial for effective thread management. Each state plays an important role in controlling the execution flow of a thread, and knowing how to handle them can help developers write better, more efficient multithreaded applications. By mastering the various thread states, you can ensure that your Java programs handle concurrency in a clean, optimized manner.