In Java, threads go through various stages during their life cycle. Understanding the life cycle of a thread is crucial for managing concurrency and ensuring efficient multithreading. The thread life cycle consists of several states, and a thread can move between these states based on certain events. In this article, we will explore the different states of a thread, how a thread transitions between these states, and the associated methods for controlling thread execution.
A thread in Java can be in one of the following states during its life cycle:
start()
method is called. It is eligible for
CPU scheduling but is not necessarily running at the moment.join()
or wait()
).sleep()
or join(milliseconds)
).
When you create a thread in Java, it is in the "new" state. The thread is just an instance of the Thread
class
but has not started yet.
class MyThread extends Thread { @Override public void run() { System.out.println("Thread is running."); } } public class ThreadLifeCycleExample { public static void main(String[] args) { // Creating a new thread (new state) MyThread thread = new MyThread(); System.out.println("Thread created."); } }
In the example above, we created a thread instance, but the thread has not yet started, so it is in the "new" state.
When the start()
method is invoked on a thread, the thread enters the "runnable" state. It is now eligible to
execute, although the thread scheduler determines when the thread will actually run.
class MyThread extends Thread { @Override public void run() { System.out.println("Thread is running."); } } public class ThreadLifeCycleExample { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); // Starts the thread and moves to runnable state System.out.println("Thread started."); } }
In this example, calling thread.start()
transitions the thread from the "new" state to the "runnable" state.
The thread is now eligible to run, though it may not immediately execute.
A thread enters the "blocked" state when it is waiting to acquire a lock for accessing a synchronized method or block. This happens when another thread is already executing inside the synchronized block or method.
class MyThread extends Thread { synchronized public void run() { try { System.out.println(Thread.currentThread().getName() + " is executing."); Thread.sleep(1000); // Simulating work } catch (InterruptedException e) { e.printStackTrace(); } } } public class ThreadLifeCycleExample { public static void main(String[] args) throws InterruptedException { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); thread1.start(); // Thread 1 starts and acquires the lock thread2.start(); // Thread 2 will enter blocked state as thread1 holds the lock } }
In this example, thread 2 enters the blocked state because thread 1 holds the lock for the synchronized run()
method.
A thread enters the "waiting" state when it is waiting indefinitely for another thread to perform a specific action. This
usually happens when a thread calls wait()
, join()
, or other similar methods.
class MyThread extends Thread { @Override public void run() { try { Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + " is running."); } catch (InterruptedException e) { e.printStackTrace(); } } } public class ThreadLifeCycleExample { public static void main(String[] args) throws InterruptedException { MyThread thread1 = new MyThread(); MyThread thread2 = new MyThread(); thread1.start(); thread1.join(); // Main thread waits for thread1 to finish (thread1 enters waiting state) thread2.start(); } }
In this example, the main
thread calls thread1.join()
, causing it to wait until thread1
finishes.
A thread enters the "timed waiting" state when it is waiting for a specific amount of time, such as when using methods like
sleep(milliseconds)
or join(milliseconds)
.
class MyThread extends Thread { @Override public void run() { try { Thread.sleep(2000); // Thread enters timed waiting state for 2 seconds System.out.println(Thread.currentThread().getName() + " is running after sleep."); } catch (InterruptedException e) { e.printStackTrace(); } } } public class ThreadLifeCycleExample { public static void main(String[] args) throws InterruptedException { MyThread thread = new MyThread(); thread.start(); } }
In this example, the thread enters the "timed waiting" state when it calls Thread.sleep(2000)
and waits for 2 seconds.
A thread enters the "terminated" state when its execution is complete. This can happen either because the run()
method
finishes its task or because the thread is terminated by an exception or a call to interrupt()
.
class MyThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName() + " has finished execution."); } } public class ThreadLifeCycleExample { public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); } }
In this example, after executing the run()
method, the thread transitions to the terminated state.
Understanding the life cycle of a thread is essential for managing and controlling thread execution in Java. The thread life cycle consists of various states, including new, runnable, blocked, waiting, timed waiting, and terminated. By understanding these states and knowing how to control them, you can write more efficient and predictable multithreaded applications in Java.