AlgoMaster Logo

Thread Lifecycle

13 min readUpdated December 17, 2025
Listen to this chapter
Unlock Audio

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:

  • The states a thread can be in
  • How threads transition between states
  • What triggers each transition
  • How to observe thread states
  • Common patterns and pitfalls

1. Thread States Overview

A thread can be in one of the following states:

State
Description

New

Thread object created, but start() not yet called

Runnable

Ready to run or currently running

Blocked

Waiting to acquire a lock

Waiting

Waiting indefinitely for another thread

Timed Waiting

Waiting for a specified time

Terminated

Execution completed

2. State 1: New

A thread is in the New state when you create a Thread object but haven't called start() yet.

In this state:

  • The thread object exists in memory
  • No system resources are allocated for execution
  • The thread has not begun executing

A thread can only be started once. Calling start() on an already-started thread throws IllegalThreadStateException.

3. State 2: Runnable

When you call start(), the thread moves to the Runnable state. This state actually encompasses two sub-states:

  1. Ready: Waiting for CPU time
  2. Running: Currently executing on a CPU core

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.

4. State 3: Blocked

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:

  • The lock becomes available
  • The thread successfully acquires it

You cannot interrupt a thread that is blocked waiting for a lock. It will wait until the lock is released.

5. State 4: Waiting

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()

wait() and notify()

The wait() method releases the lock and waits until another thread calls notify() or notifyAll() on the same object.

join()

The join() method makes the current thread wait until the target thread terminates.

6. State 5: Timed Waiting

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)

Difference: Waiting vs Timed Waiting

Scroll
Aspect
Waiting
Timed Waiting

Duration

Indefinite

Specified timeout

Exit condition

Explicit signal required

Timeout OR signal

Risk

Can wait forever if not notified

Will eventually resume

Use case

Event-driven waiting

Polling, timeouts

7. State 6: Terminated

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:

  • It cannot be restarted
  • Calling start() throws IllegalThreadStateException
  • The Thread object can still be referenced (to get final state, name, etc.)
  • Resources are eventually garbage collected

8. Complete State Transition Diagram

Here's the complete picture of all state transitions:

Transition Summary Table

Scroll
From
To
Trigger

NEW

RUNNABLE

start()

RUNNABLE

BLOCKED

Enter synchronized block, lock unavailable

BLOCKED

RUNNABLE

Lock becomes available

RUNNABLE

WAITING

wait(), join(), park()

WAITING

RUNNABLE

notify(), notifyAll(), thread terminates, unpark()

RUNNABLE

TIMED_WAITING

sleep(ms), wait(ms), join(ms)

TIMED_WAITING

RUNNABLE

Timeout, notify(), interrupt()

RUNNABLE

TERMINATED

run() completes or throws exception

9. Observing Thread States

You can check a thread's current state using getState():

Output:

10. Interrupting Threads

Interruption is the standard way to signal a thread that it should stop. It affects threads in WAITING and TIMED_WAITING states:

Interrupt Behavior by State

State
Effect of interrupt()

NEW

Sets interrupt flag (no immediate effect)

RUNNABLE

Sets interrupt flag

BLOCKED

Sets interrupt flag (does NOT unblock)

WAITING

Throws InterruptedException, clears flag

TIMED_WAITING

Throws InterruptedException, clears flag

TERMINATED

No effect

11. Common Patterns

Pattern 1: Graceful Shutdown

Use the interrupt flag to stop a thread gracefully:

Pattern 2: Waiting with Timeout

Avoid waiting forever by using timeouts:

Pattern 3: Join with Timeout

Don't wait forever for another thread:

12. Common Pitfalls

Pitfall 1: Calling run() Instead of start()

Pitfall 2: Busy Waiting

Pitfall 3: Ignoring InterruptedException

Pitfall 4: Not Using Loops with wait()

13. Summary

A thread moves through these states during its lifecycle:

Key Takeaways:

  1. NEW: Created but not started
  2. RUNNABLE: Eligible to run (ready or running)
  3. BLOCKED: Waiting for a monitor lock
  4. WAITING: Waiting indefinitely for another thread
  5. TIMED_WAITING: Waiting with a timeout
  6. TERMINATED: Execution completed

Best Practices:

  • Always use start(), never run() directly
  • Use while loops with wait() to handle spurious wakeups
  • Always restore interrupt status after catching InterruptedException
  • Prefer timed waits over indefinite waits
  • Use getState() for debugging, not for control flow

Understanding thread states helps you debug concurrency issues, reason about thread behavior, and write correct multithreaded code.