Walk through the complete lifecycle of a thread from NEW to TERMINATED. What triggers the start and end transitions, and why can't a thread be restarted?
answer
- NEW --start()--> RUNNABLE --run() ends--> TERMINATED
- Middle states (BLOCKED/WAITING/TIMED_WAITING) loop back to RUNNABLE
- start() spawns a thread; run() does not
- Restart throws IllegalThreadStateException
- TERMINATED is one-way; create a new Thread to run again
basics
~20 sA thread starts in NEW after you create it. Calling start() moves it to RUNNABLE. While running it may pass through BLOCKED, WAITING, or TIMED_WAITING. When run() finishes (or throws) it becomes TERMINATED. A terminated thread can't be started again.
solid answer
~50 sA thread begins life as NEW the moment you construct the Thread object — no OS thread exists yet. Calling start() (not run()!) creates the underlying OS thread and moves it to RUNNABLE, where the scheduler may run it. During execution it can dip into BLOCKED (waiting for a monitor), WAITING (wait/join/park), or TIMED_WAITING (sleep, timed wait/join), returning to RUNNABLE when unblocked. When the run() method returns normally or propagates an exception, the thread enters TERMINATED — it's dead. You cannot revive it: calling start() on a NEW thread is fine exactly once, but calling start() on a thread that has already started or terminated throws IllegalThreadStateException, because the OS thread and its stack are gone. Calling run() directly, by contrast, just executes the code on the current thread and never creates a new one — a classic beginner mistake.
code
java · 10 linesThread t = new Thread(() -> System.out.println("working"));
System.out.println(t.getState()); // NEW
t.start(); // -> RUNNABLE (a real OS thread now runs run())
// t.run(); // WRONG: would run on the current thread, no concurrency
t.join(); // wait until it finishes
System.out.println(t.getState()); // TERMINATED
t.start(); // throws IllegalThreadStateException — cannot restartgo deeper
Can trace NEW → RUNNABLE → TERMINATED and knows start() launches a thread while run() does not.
Adds the middle states and their return-to-RUNNABLE transitions, and explains why start() can only be called once.
Connects the no-restart rule to thread-pool design (reusable looping workers) and exception propagation out of run().
Reasons about lifecycle in terms of resource cost (OS thread creation), pool sizing, and how virtual threads change the create/terminate economics.
## The lifecycle as a journey Think of a thread as having a birth, an active life, and a death — and Java tracks where it is with `Thread.State`. ### 1. Birth: NEW When you write `Thread t = new Thread(runnable);`, you have only a Java object. State = **NEW**. No operating-system thread exists yet; nothing executes. You can configure it here (name, daemon flag, priority). ### 2. Launch: start() → RUNNABLE Calling `t.start()` does two things: it asks the OS to create a real thread, and it arranges for that thread to run the `run()` method. The state becomes **RUNNABLE** — meaning eligible to run; the OS scheduler decides when it's actually on a CPU. > **Critical distinction:** `start()` ≠ `run()`. `start()` spawns a new thread that executes `run()`. Calling `run()` *directly* just invokes it like an ordinary method on the **current** thread — no new thread, no concurrency. This is a frequent bug. ### 3. Active life: oscillating through the middle states A RUNNABLE thread doesn't stay running uninterrupted. It can transition to: - **BLOCKED** — trying to enter a `synchronized` region whose monitor is held. - **WAITING** — it called `wait()`, `join()`, or `park()` with no timeout. - **TIMED_WAITING** — it called `sleep(ms)`, a timed `wait(ms)`/`join(ms)`, or `parkNanos`. Each of these eventually transitions back to **RUNNABLE** when the cause clears (lock acquired, notified, joined-thread done, timeout elapsed, unparked). A thread may cycle through these many times during its life. ### 4. Death: TERMINATED When `run()` **returns normally** or **throws an exception that propagates out of run()**, the thread finishes and enters **TERMINATED**. The OS thread is reclaimed and the call stack is gone. `isAlive()` now returns false. ### 5. Why no restart A `Thread` object can only be `start()`ed **once**. The state machine is one-directional toward TERMINATED; there is no transition back to NEW or RUNNABLE. Calling `start()` on a thread that is not NEW throws **`IllegalThreadStateException`**, because the underlying OS thread and stack no longer exist and the state machine forbids it. If you need to 'run it again', create a *new* `Thread` (which is exactly why thread pools reuse worker threads that loop over tasks rather than restarting threads). ## Summary of triggers | Transition | Trigger | |---|---| | NEW → RUNNABLE | `start()` | | RUNNABLE → BLOCKED | contended `synchronized` entry | | RUNNABLE → WAITING | `wait()`/`join()`/`park()` (no timeout) | | RUNNABLE → TIMED_WAITING | `sleep`/`wait(ms)`/`join(ms)`/`parkNanos` | | BLOCKED/WAITING/TIMED_WAITING → RUNNABLE | lock acquired / notified / joined-thread dead / timeout / unpark | | RUNNABLE → TERMINATED | `run()` returns or throws |
- What happens if you call start() twice on the same Thread?The second call throws IllegalThreadStateException, because the thread is no longer NEW. A Thread can be started exactly once.
- How do thread pools avoid the 'can't restart' limitation?Pool worker threads never terminate per task; they loop, pulling tasks from a queue and executing them, so the same RUNNABLE thread is reused across many tasks.
saying these in an interview costs you the question
- Saying calling run() starts a new thread — it runs on the current thread.
- Believing a thread can go back to NEW or be restarted after TERMINATED.
- Thinking start() can be called twice safely.
- Confusing isAlive() false (terminated) with the ability to restart.