Coordination: wait/notify, suspend/resume, interrupts
◈ 6 cardsGuarded 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 fromwait().
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.