- Understand the six thread states in Java
- Learn state transitions and their triggers
- Master thread interruption and priority
- Know how to check and monitor thread states
Thread Lifecycle and States
A thread doesn't just run—it goes through states from creation to termination. Understanding this lifecycle is essential for debugging and using thread coordination correctly.
The Thread Lifecycle
A Java thread can be in one of six states, defined in Thread.State:
NEW
│
▼ start()
RUNNABLE ◄──────────────────────────────┐
│ │
├─── synchronized ───► BLOCKED ──────┤
│ (waiting │
│ for lock) │
│ │
├─── wait()/join() ──► WAITING ──────┤
│ (indefinite) │
│ │
├─── sleep()/wait(t) ► TIMED_WAITING─┤
│ (with timeout)│
│ │
▼ run() completes │
TERMINATED ◄──────────────────────────────┘
State Descriptions
NEW
A thread that has been created but not yet started:
Thread thread = new Thread(() -> System.out.println("Hello"));
System.out.println(thread.getState()); // NEW
// Thread exists but hasn't been started
// Not consuming CPU resources
RUNNABLE
A thread that is ready to run or currently running:
Thread thread = new Thread(() -> {
while (true) {
// Busy work
}
});
thread.start();
Thread.sleep(100);
System.out.println(thread.getState()); // RUNNABLE
Note: RUNNABLE includes both:
- Ready: Waiting for CPU time
- Running: Currently executing on a CPU
The JVM doesn't distinguish between these sub-states - both are RUNNABLE.
BLOCKED
A thread waiting to acquire a lock held by another thread:
Object lock = new Object();
Thread holder = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(5000); // Hold lock for 5 seconds
} catch (InterruptedException e) {}
}
});
Thread waiter = new Thread(() -> {
synchronized (lock) { // Will block until holder releases
System.out.println("Got the lock!");
}
});
holder.start();
Thread.sleep(100); // Let holder acquire lock
waiter.start();
Thread.sleep(100);
System.out.println(waiter.getState()); // BLOCKED
WAITING
A thread waiting indefinitely for another thread to perform an action:
Object lock = new Object();
Thread waiter = new Thread(() -> {
synchronized (lock) {
try {
lock.wait(); // Wait indefinitely
} catch (InterruptedException e) {}
}
});
waiter.start();
Thread.sleep(100);
System.out.println(waiter.getState()); // WAITING
A thread enters WAITING by calling:
Object.wait()(without timeout)Thread.join()(without timeout)LockSupport.park()
TIMED_WAITING
A thread waiting for a specified time:
Thread sleeper = new Thread(() -> {
try {
Thread.sleep(10000); // Sleep for 10 seconds
} catch (InterruptedException e) {}
});
sleeper.start();
Thread.sleep(100);
System.out.println(sleeper.getState()); // TIMED_WAITING
A thread enters TIMED_WAITING by calling:
Thread.sleep(long millis)Object.wait(long timeout)Thread.join(long millis)LockSupport.parkNanos(long nanos)LockSupport.parkUntil(long deadline)
TERMINATED
A thread that has completed execution:
Thread thread = new Thread(() -> System.out.println("Done!"));
thread.start();
thread.join(); // Wait for completion
System.out.println(thread.getState()); // TERMINATED
A thread terminates when:
- The
run()method completes normally - The
run()method throws an uncaught exception - The thread is stopped (deprecated)
State Transition Diagram
public class ThreadStateDemo {
public static void main(String[] args) throws InterruptedException {
Object lock = new Object();
Thread thread = new Thread(() -> {
// 1. Will enter BLOCKED waiting for lock
synchronized (lock) {
try {
// 3. Will enter WAITING
lock.wait();
} catch (InterruptedException e) {}
}
// 4. Back to RUNNABLE, then TERMINATED
});
System.out.println("Created: " + thread.getState()); // NEW
synchronized (lock) {
thread.start();
Thread.sleep(100);
System.out.println("Started, waiting for lock: " + thread.getState()); // BLOCKED
}
Thread.sleep(100);
System.out.println("Got lock, now waiting: " + thread.getState()); // WAITING
synchronized (lock) {
lock.notify(); // Wake up the thread
}
thread.join();
System.out.println("Finished: " + thread.getState()); // TERMINATED
}
}
Thread Methods and State Changes
| Method | Current State | New State |
|---|---|---|
new Thread() |
- | NEW |
start() |
NEW | RUNNABLE |
synchronized (lock unavailable) |
RUNNABLE | BLOCKED |
synchronized (lock acquired) |
BLOCKED | RUNNABLE |
wait() |
RUNNABLE | WAITING |
wait(timeout) |
RUNNABLE | TIMED_WAITING |
notify()/notifyAll() |
WAITING | RUNNABLE (after getting lock) |
sleep(timeout) |
RUNNABLE | TIMED_WAITING |
join() |
RUNNABLE | WAITING |
join(timeout) |
RUNNABLE | TIMED_WAITING |
run() completes |
RUNNABLE | TERMINATED |
Checking Thread State
Thread thread = new Thread(() -> { /* work */ });
// Check current state
Thread.State state = thread.getState();
// State comparisons
if (state == Thread.State.NEW) {
thread.start();
}
if (state == Thread.State.TERMINATED) {
System.out.println("Thread has finished");
}
// Check if alive (not NEW or TERMINATED)
if (thread.isAlive()) {
System.out.println("Thread is running or blocked");
}
Interrupting Threads
Thread interruption is a cooperative mechanism to signal a thread should stop:
Thread worker = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
// Do some work
Thread.sleep(1000);
} catch (InterruptedException e) {
// Interrupted during sleep
System.out.println("Interrupted! Cleaning up...");
Thread.currentThread().interrupt(); // Restore flag
break;
}
}
System.out.println("Thread exiting gracefully");
});
worker.start();
Thread.sleep(3000); // Let it run for 3 seconds
worker.interrupt(); // Request stop
worker.join();
Interrupt Methods
| Method | Description |
|---|---|
interrupt() |
Sets the interrupted flag; wakes sleeping/waiting threads |
isInterrupted() |
Checks interrupted flag without clearing it |
Thread.interrupted() |
Checks and clears interrupted flag (static method) |
Thread Priority
Thread priority is a hint to the scheduler:
Thread highPriority = new Thread(task);
Thread lowPriority = new Thread(task);
highPriority.setPriority(Thread.MAX_PRIORITY); // 10
lowPriority.setPriority(Thread.MIN_PRIORITY); // 1
// Normal priority
Thread normal = new Thread(task);
normal.setPriority(Thread.NORM_PRIORITY); // 5 (default)
Warning: Priority is just a hint. The OS scheduler makes final decisions. Don't rely on priority for correctness.
Best Practices
1. Prefer Interruption Over Deprecated Methods
// AVOID (deprecated, dangerous)
thread.stop();
thread.suspend();
thread.resume();
// USE (cooperative interruption)
thread.interrupt();
// Thread should check isInterrupted() and exit gracefully
2. Always Handle InterruptedException
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// Option 1: Restore interrupt status
Thread.currentThread().interrupt();
// Option 2: Propagate the exception
throw new RuntimeException("Interrupted", e);
// NEVER just ignore it!
}
3. Check State Before Actions
// Don't assume thread state
if (thread.getState() == Thread.State.NEW) {
thread.start();
} else if (thread.getState() == Thread.State.TERMINATED) {
// Create a new thread - can't restart terminated thread
thread = new Thread(task);
thread.start();
}
Thread states: NEW (created), RUNNABLE (ready/running), BLOCKED (waiting for lock), WAITING (waiting for signal), TIMED_WAITING (waiting with timeout), TERMINATED (done). A thread can only be started once. RUNNABLE means eligible to run, not necessarily running. Use interruption for cooperative cancellation. Thread priority is a hint, not a guarantee. Never use deprecated stop/suspend/resume.
