Thread lifecycle in Java
NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED.
Six states
t.getState() returns one of six values: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED. The happy path is NEW → RUNNABLE → TERMINATED: NEW means created but start() not called yet; TERMINATED means run() has finished. A TERMINATED thread can never be restarted.
Your turn
What does this print?
Thread t = new Thread(() -> {});
t.start();
t.join();
System.out.println(t.getState());
System.out.println(t.isAlive());TERMINATED falseRUNNABLE trueNEW false
Show the answer
join() only returns once the thread has finished, so its state is guaranteed to be TERMINATED, and it's no longer alive. Had you called getState() before start(), you'd have seen NEW.
RUNNABLE means running... right?
t.getState() returns RUNNABLE. Is t executing on a CPU core at this exact instant?
Think about it, then reveal the answer
Not necessarily. RUNNABLE means *eligible* to run: it may be on a core, or ready and waiting for the OS scheduler to give it one (it even covers threads blocked in native I/O). The OS, not Java, decides who actually gets a core.
Three ways to wait
BLOCKED: waiting to *acquire* a monitor lock to enter synchronized. WAITING: parked with no timeout: join(), wait(), LockSupport.park(). TIMED_WAITING: parked with a timeout: sleep(500), join(1000), wait(100).
synchronized (lock) { } // BLOCKED if taken
other.join(); // WAITING
lock.wait(); // WAITING
Thread.sleep(500); // TIMED_WAITING
other.join(1000); // TIMED_WAITINGBLOCKED is not "waiting"
Intuition says a thread waiting for a lock is WAITING. It isn't: it's BLOCKED. BLOCKED is reserved for waiting to enter a monitor. A thread that parked itself with wait() or join() is WAITING. (After notify(), a waiter may briefly turn BLOCKED while it re-acquires the lock.)
Reading a thread dump
Run jstack <pid> and every thread shows its state. 200 threads BLOCKED "waiting to lock <0x7a1>"? One thread holds that lock too long, or is stuck, and the rest queue behind it. Search the dump for who owns <0x7a1>: that's your bottleneck. Many TIMED_WAITING in sleep are usually idle pollers.
Key takeaways
- Happy path: NEW → RUNNABLE → TERMINATED
- BLOCKED = waiting to acquire a monitor lock
- WAITING has no timeout (join, wait); TIMED_WAITING does (sleep)
- A TERMINATED thread can never be restarted
The states are a real enum, Thread.State, and jstack prints the same names, e.g. "java.lang.Thread.State: BLOCKED (on object monitor)".
Practice questions
What does this print?
Thread t = new Thread(() -> {});
System.out.println(t.getState());
t.start();
t.join();
System.out.println(t.getState());- NEW TERMINATED
- NEW RUNNABLE
- RUNNABLE TERMINATED
- WAITING TERMINATED
Check your answer
NEW TERMINATED. Before start() a thread is NEW. join() returns only after the thread has finished, so its state is then TERMINATED.
During an outage, a thread dump shows 200 request threads in state BLOCKED, all "waiting to lock <0x7a1>". What is most likely?
- One thread holds that lock for a long time (or is stuck), and the rest queue behind it
- The threads are sleeping between retries
- The threads have finished and are waiting for garbage collection
- The OS paused them because the CPU is overloaded
Check your answer
One thread holds that lock for a long time (or is stuck), and the rest queue behind it. BLOCKED specifically means waiting to acquire a monitor. Find the thread that owns <0x7a1> in the dump — that's your bottleneck.