Memra

Coordination: wait/notify, suspend/resume, interrupts

◈ 6 cards

Guarded blocks with wait/notify, cooperative suspend/resume, interrupt() and join() — and why Thread.suspend() is deprecated.

Guarded blocks: waiting for a condition

Sometimes a thread must wait until another thread changes shared state — a consumer must wait for the producer. Busy-waiting in a while (!ready) {} loop wastes the CPU. The cooperative tools are Object.wait(), notify(), and notifyAll(). They are methods on Object and must be called while holding that object's monitor (inside a synchronized block on the same object), or you get IllegalMonitorStateException.

  • wait() releases the lock and parks the thread until it is notified.
  • notify() wakes one waiting thread; notifyAll() wakes all of them. The woken thread must re-acquire the lock before returning from wait().

The canonical guarded block always waits in a while loop, never an if, to defend against spurious wakeups and stale conditions:

synchronized (lock) {
    while (!ready) {        // while, NOT if
        lock.wait();         // releases lock, sleeps until notified
    }
    // ... condition is now true; act on it ...
}

The notifying side mutates the state and signals while holding the same lock:

synchronized (lock) {
    ready = true;
    lock.notifyAll();   // wake the waiters
}

Cooperative suspend/resume — the Greenhouse Part 2 pattern

The project must suspend and resume a whole collection of running event threads. The wrong way is the deprecated Thread.suspend() / Thread.resume(): suspend() freezes a thread without releasing its locks, so any thread needing those locks blocks forever — a classic deadlock, which is exactly why those methods are deprecated.

The correct, cooperative pattern is a guarded block driven by a flag: the thread checks a suspended flag at a safe point and wait()s while it is set; a resume() clears the flag and notifyAll()s.

class SuspendableEvent implements Runnable {
    private boolean suspended = false;
    private final Object monitor = new Object();

    public void run() {
        while (true) {
            synchronized (monitor) {
                while (suspended) {     // park at a safe point
                    try { monitor.wait(); }
                    catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        return;
                    }
                }
            }
            doOneStep();
        }
    }
    public void suspend() { synchronized (monitor) { suspended = true; } }
    public void resume() {
        synchronized (monitor) { suspended = false; monitor.notifyAll(); }
    }
}

interrupt() and join()

interrupt() is the polite way to ask a thread to stop. It sets the thread's interrupt status; a thread blocked in wait(), sleep(), or join() throws InterruptedException so it can clean up and exit. A compute-bound thread should poll Thread.currentThread().isInterrupted() and bail out. The idiom on catching InterruptedException is to restore the flag (Thread.currentThread().interrupt()) unless you are the thread's top level, so callers can see it.

join() makes one thread wait for another to finish: t.join() blocks the caller until t terminates — the standard way to wait for worker threads before reading their results.

t1t2t3t4t5eventwait()parkedre-teststepcallersuspend()resume()wait() releases the monitor while parked.
The SuspendableEvent handshake. wait() gives the monitor back while the thread is parked, which is exactly what lets resume() acquire it and notify — and why the woken thread must re-test the flag before stepping again.
holdswantsT1frozen by suspend()monitornever releasedT2blocked foreversuspend() freezes a thread without releasing its locks.
Why Thread.suspend() is deprecated. The frozen thread keeps the monitor, so every thread that needs it waits with no one left to release it. Cooperative wait() cannot produce this picture, because waiting hands the monitor back.
NORMAL ~/memra/learn/comp-308/coordination-wait-notify-interrupts utf-8 LF