: The coordination of multiple threads or processes to ensure they access shared resources in a controlled manner, preventing data corruption and race conditions
🧠 Why Synchronization Matters
Shared amount = 10
Process A reads amount → adds 2 → writes 12
Process B reads amount → adds 5 → writes 15
If both access at the same time → result may be incorrect
🧩 Key Concepts
Shared Resource: can't be accessed concurrently without synchronization
ex) global variable, files, I/O devices, or secondary memory
Critical Section: code block that accesses or modifies shared resource
⚠️ Problems Without Synchronization
Race condition**: Occurs when two or more processes access the critical section simultaneously, causing unpredictable or incorrect results
🧮 How Race Conditions Happen
High-level operations are translated into machine codes(instructions)
ex) "Increment a by 1"
r1 = a // store a into register
r1 = r1 + 1 // increment register
a = r1 // store register into variable
If a context switching occurs between these steps, it may corrupt the result.
✅ Conditions for Proper Synchronization (Mutual Exclusion)
1. Mutual exclusion: only one process may be in critical section at a time
2. Progress: if no process is in the critical section, the next eligible proces should be allowed to enter without unnecessary delay
3. Bounded waiting: A process waiting to enter critical section will eventually get its turn
1. Mutex Lock — for mutual exclusion
it is like a lock in one changing room
bool lock = false;
acquire() {
while (lock == true) {// if it is locked
// keep checking whether it is locked still
}
// if it is not locked, lock it
lock = true;
}
release() {
lock = false // unlock it after get out of critical section
}
acquire() // check whether it is locked, if it not, lock it
{enter the critical section}
release() // unlock it
Busy waiting wastes CPU cycles while contantly checking the lock
**2. Semaphore — for managing multiple resource units
a. with busy waiting
wait() {
while(S <= 0) // if there is no available resources
{
// keep checking whether any resources are available
}
S--; // Use that resource and enter the critical section
}
signal() {
S++; // After critical section, release S
}
=> Busy waiting wastes CPU
b. without busy waiting, use a waiting queue (a block queue)
wait() {
S--;
while(S < 0) // if there is no available resources
{
Add this process to Queue;
sleep(); // process is in a waiting queue
}
}
signal() {
S++; // After critical section, release S
if (S <= 0) {
remove a proess p from a waiting queue
wakeup(p) // move p to ready queue
]
}
Both usage is the same
wait()
{Enter the critical section}
signal()

Think of it as a manager for accessing shared resources through defined interfaces.
shared resource (e.g., a file, printer, buffer)
interfaces (methods) — like read(), write(), access(), etc.
🔐 Mutual Exclusion
a. Only one process can enter the monitor(critical section) at a time
b. Other processes wait in the monitor entry queue
🧭 Condition Variables (for execution order)
--> x.wait()
: process gives up the monitor and waits on condition x
--> x.signal()
monitor Buffer {
condition notEmpty;
void consume() {
if (buffer is empty) {
notEmpty.wait(); // ❗ Wait until producer adds something
}
// Consume item
}
void produce(item) {
// Add item to buffer
notEmpty.signal(); // ✅ Notify consumer that item is available
}
}
A condition variable is a special object used for:
Putting a process to sleep (wait())
Waking up a sleeping process (signal())