When you create a thread, it doesn't start running immediately. When it's running, it might pause to wait for a lock or sleep for a duration. When it finishes, it doesn't disappear instantly.
A thread goes through several states during its lifetime. Understanding these states helps you write correct concurrent code, debug threading issues, and answer interview questions about multithreading.
In this article, we'll explore:
A thread can be in one of the following states:
The key insight: A thread in the "Runnable" state isn't necessarily running. It means the thread is eligible to run, and the OS scheduler decides when it actually gets CPU time.
A thread is in the New state when you create a Thread object but haven't called start() yet.
In this state:
A thread can only be started once. Calling start() on an already-started thread throws IllegalThreadStateException.
When you call start(), the thread moves to the Runnable state. This state actually encompasses two sub-states:
The OS scheduler constantly switches between threads, giving each a slice of CPU time. Your code cannot distinguish between "ready" and "running" because this is managed by the operating system.
A thread enters the Blocked state when it tries to acquire a lock (monitor) that another thread holds.
A thread exits the Blocked state only when:
You cannot interrupt a thread that is blocked waiting for a lock. It will wait until the lock is released.
A thread enters the Waiting state when it waits indefinitely for another thread to perform an action. This happens when calling:
Object.wait() (without timeout)Thread.join() (without timeout)LockSupport.park()The wait() method releases the lock and waits until another thread calls notify() or notifyAll() on the same object.
The join() method makes the current thread wait until the target thread terminates.
A thread enters the Timed Waiting state when it waits for a specified amount of time. This happens when calling:
Thread.sleep(millis)Object.wait(timeout)Thread.join(timeout)LockSupport.parkNanos(nanos)LockSupport.parkUntil(deadline)A thread enters the Terminated state when its run() method completes, either normally or due to an exception.
A thread can also terminate due to an uncaught exception:
Once a thread is terminated:
start() throws IllegalThreadStateExceptionHere's the complete picture of all state transitions:
You can check a thread's current state using getState():
Output:
Interruption is the standard way to signal a thread that it should stop. It affects threads in WAITING and TIMED_WAITING states:
Use the interrupt flag to stop a thread gracefully:
Avoid waiting forever by using timeouts:
Don't wait forever for another thread:
A thread moves through these states during its lifecycle:
Key Takeaways:
Best Practices:
start(), never run() directlywhile loops with wait() to handle spurious wakeupsInterruptedExceptiongetState() for debugging, not for control flowUnderstanding thread states helps you debug concurrency issues, reason about thread behavior, and write correct multithreaded code.