Driver FixRecommendedSound, Wi-Fi or graphics acting up? Check drivers firstFind missing or outdated drivers fast.Check DriversOctober DealsAmazon USOctober deal check: compare before you payAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
World desk6 min

What Is a Memory Model in Concurrent Programming? Rules for Go, Java, C++ and Rust

A memory model is the language contract for concurrent reads and writes. Learn how happens-before, synchronization and atomics affect Go, Java, C++ and Rust.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A memory model is a programming language’s contract for what concurrent code is allowed to observe. To reason reliably about shared data, identify the synchronization action that orders a write before another thread or goroutine reads it; source-code order in one thread, or an atomic access by itself, does not establish that relationship.

What a memory model guarantees

Concurrent programs can perform reads and writes in multiple threads or goroutines, while compilers and processors may reorder operations as long as the language’s rules are respected. A memory model defines which results of those executions are permitted. It gives meaning to program order, synchronization, visibility, atomic operations and data races.

As an Amazon Associate I earn from qualifying purchases.

It is a language-level contract, not a description of a particular processor’s cache behavior. Reason from the rules of the language and version you are using, rather than assuming that a write becomes visible to another thread simply because it has run first on the hardware.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

What “happens-before” means

Happens-before is a way to establish ordering across concurrent execution. If action A happens-before action B, the language model orders A before B. The relationship is built from operations that are ordered within one execution thread, synchronization between threads, and transitivity: if A happens-before B and B happens-before C, then A happens-before C.

Go’s memory model describes happens-before as the transitive closure of sequenced-before and synchronized-before relations. C++ defines it through sequencing, synchronization and transitivity. In either case, two statements that happen to be in separate threads do not gain a cross-thread ordering edge merely because one appears earlier in the source file.

A practical way to check visibility

  1. Find the shared data. Identify which thread writes it and which other thread reads or modifies it.
  2. Find the synchronization operation. Look for a channel operation, mutex, volatile or other language-defined synchronization action, or an atomic operation with appropriate ordering.
  3. Trace the edge. Verify that the receiving operation is connected to the publishing operation under that language’s rules. For acquire/release atomics, for example, the acquire must observe the release for the described ordering to apply.
  4. Check every conflicting access. If another path reads or writes the same data without the required synchronization, the fact that one path is ordered does not make all access safe.
  5. Check the program’s logic separately. A correctly synchronized program can still implement the wrong algorithm.

When acquire and release help

Acquire and release are useful when one thread publishes data for another without protecting every access with a lock. The publishing thread first writes the data, then performs a release operation. The receiving thread performs an acquire operation that observes that release, then reads the published data. In Rust’s documented model, the earlier operations are ordered before later operations when a Release store is observed by an Acquire load.

This is a language-level ordering and visibility guarantee, not a promise to “flush the cache.” The relevant question is whether the acquire observes the release and therefore establishes the required relationship—not whether the operations have names that sound strong.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Relaxed atomics are not publication by themselves

A relaxed atomic operation is still atomic: its access to that atomic object is indivisible under the language’s rules. But Relaxed imposes no ordering constraints beyond atomic operations in Rust’s documentation. It therefore does not, on its own, publish unrelated non-atomic data to another thread. Use a synchronization mechanism that establishes the needed edge, or protect the shared state with a mutex or another suitable primitive.

Are atomic variables enough to prevent data races?

No. Atomicity and ordering answer different questions. Atomicity concerns an operation on the atomic object; ordering concerns how that operation relates to other memory accesses. A program may use an atomic flag and still access associated data incorrectly if the flag operations do not establish the needed synchronization or if other conflicting accesses bypass the protocol.

Rust explicitly defines conflicting unsynchronized accesses where at least one is non-atomic as data races, and says those races are undefined behavior. Go’s advice is to serialize access to data modified while multiple goroutines can access it, using channels or synchronization primitives such as those in sync and sync/atomic. Java’s specification also cautions that freedom from data races or sequential consistency does not make a group of operations atomic.

As a default, use higher-level synchronization that makes ownership and access boundaries clear. Reach for low-level atomics when the protocol requires them and you can identify exactly which operation creates the ordering edge.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

How Go, Java, C++ and Rust differ

These languages all specify how concurrent operations are interpreted, but their guarantees and terminology are not interchangeable. The table summarizes the scope of the official documents cited here; it is not a substitute for the language edition or library documentation applicable to a particular program.

Language Synchronization and ordering Race and correctness notes Document scope
Go Channels and synchronization primitives, including sync and sync/atomic; happens-before is derived from sequenced-before and synchronized-before. The model says programs that modify data accessed simultaneously by multiple goroutines must serialize that access. Data-race-free programs have the DRF-SC guarantee: their outcomes can be explained by a sequentially consistent interleaving. Official memory model dated June 6, 2022.
Java The Java Language Specification defines synchronization and happens-before rules for threads and memory actions. Sequential consistency and freedom from data races do not make a multi-operation group atomic; check whether the algorithm needs an operation to be indivisible as a whole. Java Language Specification, Chapter 17, version 26.
C++ Mutex operations, fences and atomic operations; its rules describe sequencing, synchronization, relaxed atomics, acquire/release and happens-before. Use the applicable standard and library documentation to determine the precise behavior of the operations in use. The cited intro.races text is from a live working draft, whose wording and clause numbering may change.
Rust Synchronization types and explicit atomic orderings: Relaxed, Release, Acquire, AcqRel and SeqCst. Conflicting unsynchronized accesses with at least one non-atomic access are data races and undefined behavior. std::sync::atomic documentation describes C++20 atomic rules without consume ordering; the ordering documentation identifies std 1.99.0.

Go: serialize shared mutation

Go’s documented starting point is to serialize access when multiple goroutines can access data being modified. A channel or synchronization primitive can establish the necessary relationship; the goal is not merely to make an individual read or write appear atomic. The model’s DRF-SC guarantee applies to data-race-free Go programs and should not be generalized to other languages.

Java: apply the JLS rules to the runtime you use

Java’s Chapter 17 specifies thread and memory semantics, including happens-before. Use the Java Language Specification version applicable to the runtime, and distinguish those language rules from details of a particular JVM implementation. Also verify whether a sequence of operations needs to be atomic as a group; race freedom alone does not provide that property.

C++: consult the published standard for production guidance

The live C++ working draft explains conflicting evaluations, mutex and atomic synchronization, acquire/release operations, relaxed atomics and happens-before. Because that draft can change, production decisions should be checked against the published C++ standard edition and library documentation relevant to the project.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Rust: choose an ordering for the protocol, not by name alone

Rust documents Relaxed, Release, Acquire, AcqRel and SeqCst orderings. Its atomic rules currently follow C++20’s rules except that Rust does not provide consume ordering. Choose the ordering that establishes the relationship your program needs; do not assume that every atomic operation orders unrelated accesses.

A reliable checklist for concurrent code

  • Name the shared state: list the data that can be accessed by more than one thread or goroutine.
  • Identify conflicting operations: find pairs of accesses where at least one modifies the same state.
  • Choose a synchronization mechanism supported by the language: often a mutex, channel or higher-level synchronization type is easier to reason about than a custom atomic protocol.
  • Prove the ordering path: point to the operation that publishes a change and the operation that receives it.
  • Keep language rules distinct: verify the exact language edition and library behavior instead of transferring assumptions from another ecosystem.
  • Review logical atomicity: decide whether a group of operations must behave as one indivisible action; a race-free implementation may still need a lock or a higher-level atomic operation for that requirement.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
PC Slower Than It Used to Be?Free scan - under a minute
Crashes, No Sound, or Screen Glitches?Free driver scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.