Learning Question
Why do running programs need locks, waits, and wakeups?
When execution overlaps, shared state can be observed or changed at the wrong time.
Synchronization coordinates access to shared state and coordinates progress between execution contexts.
The first mental model is:
Synchronization is how overlapping executions agree on when shared state may be read, changed, or waited on.
Overlap Creates Ordering Problems
In a single-threaded path, one operation follows another in program order.
With multiple threads or processes, operations can overlap.
One thread may read data while another is updating it.
One process may wait for another to produce input.
One worker may need to sleep until a queue contains work.
The problem is not merely that programs run “at the same time.”
The problem is that shared state and shared resources need clear ordering rules.
Locks Protect Critical Sections
A lock is commonly used to protect a critical section.
A critical section is a region of code that must not be executed by multiple threads at the same time for the same protected state.
The lock creates mutual exclusion.
Before entering the critical section, a thread acquires the lock.
After it finishes, it releases the lock.
This does not make the code faster.
It makes the shared state coherent by preventing conflicting overlap.
Waiting Without Spinning
Sometimes a thread cannot continue until a condition becomes true.
For example:
- a queue must become non-empty
- a child process must exit
- data must arrive on a socket
- a lock must become available
- a timer must expire
A poor approach is to spin constantly while checking the condition.
Operating systems provide ways to block a thread so it stops consuming CPU until it can make progress.
The kernel records the waiting state and later wakes the thread when the relevant event occurs.
Wakeups Need a Condition
A wakeup does not replace the condition being waited for.
A waiting thread should usually re-check the condition after waking.
There may be multiple waiters.
Another thread may consume the resource first.
Some APIs allow spurious wakeups.
Signals or interruptions may also cause a wait to return.
The durable rule is:
Wait for a condition, not merely for a wakeup event.
Race Conditions
A race condition occurs when program correctness depends on an uncontrolled timing relationship between overlapping executions.
For example, two threads may both read the same counter value, increment it, and write the same result.
One update is lost because the read-modify-write sequence was not protected.
The CPU did execute instructions.
The operating system did schedule threads.
But the program failed to define the required coordination.
Deadlock
Synchronization can create its own failures.
A deadlock occurs when execution contexts wait for each other in a cycle and none can continue.
For example, thread A holds lock 1 and waits for lock 2, while thread B holds lock 2 and waits for lock 1.
The OS may show that the threads are blocked, but it does not automatically know the program’s intended lock order.
Avoiding deadlock requires design-level discipline such as consistent lock ordering, timeouts, or simpler ownership boundaries.
Kernel Support and Library APIs
Many synchronization APIs are provided by language runtimes or libraries.
However, efficient waiting often needs operating-system support.
A lock may be acquired in user space when uncontended.
When contended, the thread may need a kernel-assisted wait and wakeup mechanism.
The exact implementation varies.
The programmer-facing model stays stable:
Synchronization combines program-level rules with OS-supported blocking and wakeup behavior.
Core Mental Model
Synchronization exists because overlapping execution needs explicit coordination.
Locks protect shared state.
Waits suspend execution until progress is possible.
Wakeups make blocked work runnable again.
When diagnosing concurrency behavior, ask:
What shared state is being protected, what condition is being waited for, and who is responsible for making that condition true?
Final Summary
Synchronization and waiting let programs coordinate overlapping execution without corrupting shared state or wasting CPU.
They connect program-level correctness rules to OS-level blocking, scheduling, and wakeup mechanisms.