Thread Lifecycle in Java

A thread moves through six states, and knowing which one it is in is the first step in diagnosing any hang.

The six states

                   start()
        NEW ---------------------> RUNNABLE
                                    |  ^  
             synchronized blocked   |  |     wait(), join(), park()
                                    v  |    v
                                 BLOCKED    WAITING
                                            |
                     sleep(t), wait(t)      v
                                        TIMED_WAITING
                                    |
                          run() ends v
                               TERMINATED
StateMeaning
NEWCreated but start() has not been called
RUNNABLEEligible to run; may or may not be on a processor right now
BLOCKEDWaiting to acquire a monitor lock
WAITINGWaiting indefinitely for another thread to act
TIMED_WAITINGWaiting with a timeout
TERMINATEDThe run method has completed or thrown
Java has no separate "running" state. RUNNABLE covers both ready and actually executing, because the JVM leaves that distinction to the operating system scheduler.

Observing the states

Object lock = new Object();

Thread worker = new Thread(() -> {
    synchronized (lock) {
        try {
            lock.wait();          // WAITING
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
});

System.out.println(worker.getState());   // NEW
worker.start();
Thread.sleep(50);
System.out.println(worker.getState());   // WAITING

synchronized (lock) {
    lock.notify();
}
worker.join();
System.out.println(worker.getState());   // TERMINATED

What moves a thread out of RUNNABLE

CallState enteredReleases the lock
Thread.sleep(ms)TIMED_WAITINGNo
object.wait()WAITINGYes
object.wait(ms)TIMED_WAITINGYes
thread.join()WAITINGNo
Entering a contended synchronized blockBLOCKEDNot applicable
lock.lock() when heldWAITINGNo

The column that matters is the last one. sleep keeps every lock it holds; wait gives up the monitor it was called on. Confusing the two produces deadlocks that are hard to explain.

wait, notify and notifyAll

public class MessageBox {

    private final Object lock = new Object();
    private String message;

    public void put(String value) {
        synchronized (lock) {
            message = value;
            lock.notifyAll();          // wake every waiter
        }
    }

    public String take() throws InterruptedException {
        synchronized (lock) {
            while (message == null) {   // always a loop, never an if
                lock.wait();
            }
            String value = message;
            message = null;
            return value;
        }
    }
}

Three rules govern these methods:

  • They may only be called while holding the monitor of that object.
  • The condition must be tested in a while loop, because a thread can wake without a matching notify. That is called a spurious wakeup, and it is permitted by the specification.
  • Prefer notifyAll. notify wakes one arbitrary waiter, which can be the wrong one and can leave the system stalled.

BLOCKED against WAITING

// BLOCKED: queued for a monitor, nothing to do but wait for the owner to leave
synchronized (lock) { }

// WAITING: voluntarily suspended until another thread signals
synchronized (lock) { lock.wait(); }

A thread that is BLOCKED is competing for a lock. A thread that is WAITING has given one up and needs to be woken. In a thread dump, many BLOCKED threads on one lock indicate contention; many WAITING threads usually indicate a missing signal.

Reading a thread dump

"worker-3" #21 prio=5 os_prio=0 tid=0x00007f... nid=0x1a03 waiting for monitor entry
   java.lang.Thread.State: BLOCKED (on object monitor)
        at com.example.notes.Counter.increment(Counter.java:14)
        - waiting to lock <0x000000076ab3f1c8> (a java.lang.Object)
        - locked <0x000000076ab3f1d0> (a java.lang.Object)

The state, the line, and the identity of the lock being waited on are all there. Two threads each holding what the other waits for is a deadlock, and the JVM reports it explicitly at the end of the dump.

Priorities are only a hint

worker.setPriority(Thread.MAX_PRIORITY);   // 10
worker.setPriority(Thread.MIN_PRIORITY);   // 1

Thread priority is passed to the operating system scheduler, which may ignore it entirely. Correctness must never depend on it.

Modern alternatives

BlockingQueue<String> queue = new LinkedBlockingQueue<>();

queue.put("message");             // blocks when full
String value = queue.take();      // blocks when empty

Almost every use of wait and notify is better expressed with a BlockingQueue, a CountDownLatch, a Semaphore or a Condition. Learn the low level mechanism to understand what happens, then use the higher level tools.

Common mistakes

  • Calling wait or notify outside a synchronized block, which throws IllegalMonitorStateException.
  • Testing the condition with if instead of while.
  • Using notify where notifyAll is needed.
  • Expecting sleep to release a lock.
  • Relying on thread priority for correctness.
  • Calling wait on one object while synchronising on another.

Best practices

  • Always wait in a loop that rechecks the condition.
  • Prefer notifyAll.
  • Prefer the java.util.concurrent utilities to wait and notify.
  • Name threads so dumps are readable.
  • Take a thread dump before guessing at the cause of a hang.

Practice

  1. Print the state of a thread before start, during a sleep, and after join.
  2. Explain the difference between BLOCKED and WAITING in your own words.
  3. Why must the condition around wait be a while loop?
  4. Implement a one slot message box, then rewrite it with an ArrayBlockingQueue.
  5. Why is it wrong to rely on setPriority to control ordering?

Conclusion

Six states, and the important distinction is whether a waiting thread still holds its lock. Learn wait and notify to understand thread dumps, and then use the higher level concurrency utilities in real code.

Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All Java notes →

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.