Java Monitor
Java’s synchronized is a no-priority nonblocking monitor, C = W < S in the monitor-type table. Because calling tasks share priority with signalled waiters, barging is allowed: a newly-arrived caller can race past a notified waiter and grab the monitor first. Every wait() must therefore sit in a while loop.
Why does Java allow barging?
Historical + JVM simplicity. No-priority nonblocking is the cheapest to implement (one queue per monitor, no stack-of-signalled-tasks bookkeeping) and fits
notify/notifyAllsemantics. The cost: every user-writtenwaitmust loop on its predicate, becausenotifyAll(the usual hammer) wakes everyone, most of whom must re-block.
Key properties
synchronizedmembers/statements ⇒ mutex members (Buhr: “incorrectly named”, it’s_Mutex, not synchronization).- One implicit condition variable per object via
wait()/notify()/notifyAll(). j.u.concurrenthas multi-conditionLock/Condition, but those are library not language, and incompatible with the implicit one.
Bounded buffer (must use while)
class Buffer {
private int count = 0;
public synchronized void insert( int elem ) {
while ( count == Size ) wait(); // while, not if — barging!
// add to buffer
count += 1;
if ( count == 1 ) notifyAll();
}
public synchronized int remove() {
while ( count == 0 ) wait();
count -= 1;
if ( count == Size - 1 ) notifyAll();
return elem;
}
}Three-stage evolution of the Java barrier (Buhr §8.11)
Stage 1, naive, broken. Nth task notifyAlls, leaves, does its ith step, barges back in before anyone restarts.
class Barrier {
private int N, count = 0;
public Barrier( int N ) { this.N = N; }
public synchronized void block() {
count += 1;
if ( count < N )
try { wait(); } catch( InterruptedException e ) {}
else // barrier full
notifyAll(); // wake all barrier tasks
count -= 1; // uncount on leaving
}
}Failure: Nth task notifies, exits, runs step i, races back into block() before the woken tasks have rescheduled. It sees count == N (they haven’t decremented yet) and enters step i+1 while others are still finishing step i.
Stage 2, barging avoidance via count reset. Nth task zeros count itself and removes the post-wait decrement.
else { // barrier full
count = 0; // reset count
notifyAll(); // wake all barrier tasks
}
// (count -= 1 removed)Still wrong: Java wait admits spurious wakeups, so the if must become a loop. But the loop condition can’t be count < N, count is always < N after the reset.
Stage 3, generation counter. Each waiter records its generation; the Nth task bumps the generation so waiters can tell “was I released, or just spuriously woken?”
class Barrier {
private int N, count = 0, generation = 0;
public Barrier( int N ) { this.N = N; }
public synchronized void block() {
int mygen = generation;
count += 1;
if ( count < N ) // barrier not full ⇒ wait
while ( mygen == generation ) // loop vs spurious wakeup
try { wait(); } catch( InterruptedException e ) {}
else { // barrier full
count = 0; // reset for next cycle
generation += 1; // advance: woken tasks bail out of while
notifyAll();
}
}
}Takeaway: what’s one line in uC++ (uBarrier::block()) needs a generation counter, a reset, and a spurious-wakeup loop in Java, the price of no-priority nonblocking + one implicit condition.
Nested-monitor condition variable, broken
Trying to simulate multi-condition with nested synchronized objects deadlocks: the inner wait() releases only the inner lock, not the outer, producers/consumers stall holding the enclosing buffer’s lock.