Monitor

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/notifyAll semantics. The cost: every user-written wait must loop on its predicate, because notifyAll (the usual hammer) wakes everyone, most of whom must re-block.

Key properties

  • synchronized members/statements ⇒ mutex members (Buhr: “incorrectly named”, it’s _Mutex, not synchronization).
  • One implicit condition variable per object via wait() / notify() / notifyAll().
  • j.u.concurrent has multi-condition Lock/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.