1. The Evolution of Synchronization
Semaphores are powerful but dangerous. A single missing V() call can freeze the entire system.
Monitors were invented to make synchronization safer and more structured.
What is a Monitor?
A Monitor is a synchronization construct that enforces:
- Mutual Exclusion: Only one thread can be active inside the monitor at a time.
- Encapsulation: Shared data is hidden and only accessible via monitor procedures.
In Java, every Object is a Monitor (via synchronized).
Condition Variables
Monitors use Condition Variables to allow threads to wait for a specific state (e.g., “Buffer Not Empty”).
- Wait(): Release the lock and go to sleep in the Wait Set.
- Signal() / Notify(): Wake up one thread from the Wait Set and move it to the Entry Set.
[!IMPORTANT] Always use
whileloops! When a thread wakes up, the condition might have changed again (MESA semantics).while (buffer.isEmpty()) { wait(); }
2. Interactive: Monitor Lifecycle
Visualize the journey of a thread through a Monitor.
- Entry Set: Waiting to acquire the lock.
- Owner: Executing inside the monitor.
- Wait Set: Waiting for a condition (released lock).
3. Go Channels: CSP
Go takes a different approach based on CSP (Communicating Sequential Processes). Instead of locking shared memory, goroutines share data by sending it through Channels.
“Do not communicate by sharing memory; instead, share memory by communicating.”
Unbuffered vs Buffered
- Unbuffered: Synchronous. Sender blocks until Receiver is ready. Handshake.
- Buffered: Asynchronous (up to capacity). Sender only blocks if buffer is full.