Acquire/release semantics with 4 threads
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Acquire and release semantics are about ordering, not about magically making every shared-memory interaction safe. A release operation publishes prior writes, and a matching acquire operation on the same synchronization chain makes those writes visible to the acquiring thread. With four threads, the key question is not “how many threads are there.” It is “which threads are connected by a happens-before relationship through the relevant atomic operations.”
The Core Rule
In C++-style memory-model terms, a store with release semantics ensures that earlier writes in that thread cannot move after the release. A load with acquire semantics ensures that later reads in the acquiring thread cannot move before the acquire.
When an acquire load reads the value written by a release store on the same atomic variable, the release synchronizes-with the acquire. That creates a happens-before edge.
That is the important mechanism. Acquire and release do not create global order across unrelated atomics automatically.
A Simple Four-Thread Example
Consider this C++ example.
Here is what matters:
- '
t1writesdata = 42before the release store toready1' - '
t2acquires fromready1, so it seest1's earlier writes' - '
t2then performs a release store toready2' - '
t3acquires fromready2, so the ordering chain is extended transitively'
As a result, t3 is allowed to observe data == 42 through the acquire-release chain from t1 to t2 to t3.
t4 is irrelevant unless it also participates in a synchronization chain.
Transitivity Is the Interesting Part
A lot of four-thread questions are really questions about transitivity. If thread B acquires from thread A and then releases to thread C, the ordering information can propagate.
That means acquire-release is not limited to direct one-producer, one-consumer pairs. It can form a chain across several threads, as long as the synchronizing operations are connected correctly.
What it does not do is guarantee anything about a thread that only touches unrelated atomics or unsynchronized shared data.
Same Atomic Variable Versus Same Ordering Chain
Developers often oversimplify the rule into “acquire and release must be on the same variable.” That is partially true for a direct synchronizes-with relation, but multi-thread chains can propagate visibility through a sequence of acquire-release edges on different atomic variables.
The real question is whether there is a valid happens-before path from the write you care about to the read you care about.
If there is no such path, the fact that four threads exist in the program does not help.
Acquire/Release Does Not Fix Data Races by Itself
Another common mistake is believing that release on one atomic makes ordinary unsynchronized shared variables safe everywhere. It only makes prior writes visible to threads that actually acquire through the relevant chain.
If another thread reads data without participating in the synchronization, that read can still be a race or can still observe stale state.
This is why memory-order reasoning must always be tied to a specific read and a specific publication path.
When You Need Stronger Ordering
Acquire-release is often enough for publication, handoff, and many lock-free coordination patterns. But if your reasoning keeps depending on “everyone should see everything in one global order,” you may actually need stronger synchronization, perhaps sequential consistency or a different design entirely.
The right memory order is chosen based on the required guarantee, not on minimalism for its own sake.
Common Pitfalls
- Thinking acquire-release creates a global ordering across all threads regardless of which atomic operations are connected.
- Forgetting that the synchronizing relation begins when an acquire actually observes a value from the corresponding release chain.
- Assuming an unrelated fourth thread gains visibility automatically just because other threads synchronized correctly.
- Using acquire-release on atomics while still performing unsynchronized reads and writes on shared data outside the happens-before path.
- Reaching for weaker memory orders before the publication and visibility requirements are fully understood.
Summary
- Release publishes prior writes, and acquire makes those published writes visible when the synchronization chain matches.
- With four threads, the important question is whether there is a happens-before path, not simply how many threads exist.
- Ordering can propagate transitively across multiple acquire-release steps.
- Unrelated threads or unrelated atomics do not automatically participate in that ordering.
- Acquire-release is powerful, but it only works when the synchronization path is explicit and correct.

