Condition Variables - Inter-Thread Communication

1. The “Why” Sometimes a thread has the lock but cannot proceed because the data isn’t ready (e.g., a Consumer finds an empty queue). The Problem: If the thread just loops (while(queue.isEmpty());), it wastes 100% of the CPU. The Solution: The thread “waits” on a condition. This releases the lock and puts the thread to sleep. When another thread makes the condition true, it “signals” (notifies) the sleeping thread to wake up and try again. 2. Comparison: wait/notify vs. Condition Object Feature Object.wait/notify java.util.concurrent.Condition Association Every Java Object has one. Associated with a ReentrantLock. Capability Only one “wait set” per object. Multiple conditions per lock (e.g., notFull and notEmpty). Control Standard synchronized blocks. Precision control with signal() and await(). Analogy A single waiting room for a whole office. Multiple specific waiting rooms (e.g., “Radiology” vs “ER”). 3. The “Golden” Snippet: Multi-Condition Producer-Consumer Using Condition objects with ReentrantLock allows us to be very specific about which threads we wake up. ...

March 27, 2026

Objects as Condition Variables - wait, notify, & notifyAll

1. The “Why” Every Java object has an Intrinsic Lock (the monitor). Along with that lock, every object maintains a Wait Set—a list of threads that are suspended and waiting for a signal related to that object. This allows threads to communicate without using complex external libraries, using only the objects they are already synchronizing on. 2. Comparison: wait/notify vs. Condition Objects Feature wait() / notify() Condition (await/signal) Origin Part of java.lang.Object (Available since JDK 1.0). Part of java.util.concurrent (Added in JDK 1.5). Locking Works with synchronized blocks. Works with ReentrantLock. Wait Sets Only one per object. Multiple per lock (e.g., notFull, notEmpty). Efficiency notifyAll() wakes everyone, even if they can’t proceed. signal() can target specific groups of threads. 3. The “Golden” Snippet: Classic Producer-Consumer In this version, we don’t need a ReentrantLock. We use the synchronized keyword and the shared object itself to coordinate. ...

March 27, 2026

Semaphore - Scalable Producer-Consumer

1. The “Why” A standard Mutex or synchronized block only allows one thread at a time. A Semaphore, however, maintains a set of permits. It is used to limit the number of concurrent threads accessing a specific resource (e.g., only 5 database connections allowed). In a Producer-Consumer scenario, we use it to signal when “Space is Available” (for the producer) and when “Items are Available” (for the consumer). 2. Comparison: Mutex vs. Semaphore Feature Mutex (or Binary Semaphore) Counting Semaphore Permit Count Exactly 1. $N$ (User-defined). Ownership Only the thread that locked it can unlock it. No ownership. Any thread can release a permit. Primary Use Protecting a Critical Section (Safety). Signalling and Resource Throttling (Coordination). Analogy A bathroom with one key. A parking lot with $N$ spots. 3. The “Golden” Snippet: Semaphore-Based Producer-Consumer This implementation uses two semaphores to coordinate a shared buffer. One tracks “Full” slots and the other tracks “Empty” slots. ...

March 27, 2026