Coordination: wait/notify, suspend/resume, interrupts
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.