Upstream v12 replaces notify() with notifyAll() on successful queue operations.
Instead of notifying one thread, successful Put and Get operations notify all waiting threads. The independent notification step from the previous attempted fix is removed.
With notify(), the JVM might wake up a thread that cannot make progress (e.g., waking a producer when the buffer is already full). That thread goes back to waiting, and the actual thread that could make progress is never woken - leading to deadlock.
notifyAll() makes all waiters eligible to reacquire the shared lock and recheck their conditions. This fixes the modeled deadlock, but waking threads that cannot proceed can add contention. It does not by itself guarantee fair scheduling or freedom from starvation.
As a bonus exercise, check if it is necessary to notify all waiting threads in both Put and Get.
The upstream tutorial notes that using notifyAll() in just one of the two operations suffices for this deadlock fix. Try keeping NotifyAll in one operation and restoring the earlier single-thread Notify in the other. The next lesson avoids waking the wrong kind of thread in the first place.
TLC completes without a deadlock or invariant violation for the supplied p4c3b3 model. This is a finite-model result, not a proof for every configuration or a liveness guarantee.