What are atomic operations for newbies?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
An atomic operation is one that completes entirely or not at all — no other thread can observe it in a partially completed state. The word "atomic" comes from Greek "atomos" (indivisible). In concurrent programming, atomicity prevents race conditions where two threads reading and writing the same data produce corrupted results. Common atomic operations include compare-and-swap (CAS), atomic increment/decrement, and atomic load/store. Most languages provide atomic primitives through standard libraries or language features.
The Problem Without Atomicity
counter += 1 looks like one operation but is actually three: read the value, add 1, write the result back. Two threads can read the same value simultaneously, both add 1, and both write back the same result — losing one increment.
Fix with Locks
A lock (mutex) ensures only one thread executes the critical section at a time. This is correct but slower than true atomic operations because of lock acquisition overhead.
Atomic Operations in Java
AtomicInteger.incrementAndGet() uses hardware compare-and-swap (CAS) instructions, which are faster than locks because they don't require context switching or blocking.
Atomic Operations in C++
C++ std::atomic<T> provides fine-grained control over memory ordering. memory_order_relaxed is the fastest option when you only need atomicity without ordering guarantees relative to other variables.
Compare-and-Swap (CAS)
CAS is the hardware primitive behind most atomic operations. It reads the current value, computes the new value, and writes it only if the current value hasn't changed since the read. If another thread modified it, CAS fails and the operation retries.
Go Atomic Operations
What Operations Are Naturally Atomic?
In most languages, reading or writing a single aligned word-sized value is atomic at the hardware level, but compound operations (read-modify-write) are never atomic without explicit synchronization.
Common Pitfalls
- Assuming compound operations are atomic:
counter++,counter += 1, andx = x + 1are all read-modify-write sequences, not atomic. Even single-line expressions can involve multiple machine instructions. - Relying on language-specific guarantees: CPython's GIL makes some operations effectively atomic, but this is an implementation detail, not a language guarantee. Code that depends on the GIL breaks on other Python implementations.
- ABA problem with CAS: CAS checks if the value is the same, but another thread might have changed it from A to B and back to A. CAS sees A and succeeds, missing the intermediate change. Use versioned references (
AtomicStampedReferencein Java) to detect this. - Memory ordering confusion: Atomic operations guarantee atomicity but not necessarily visibility across cores. In C++,
memory_order_relaxedprovides atomicity without ordering. Usememory_order_seq_cst(default) when you need operations to be visible in order. - Overusing atomics instead of higher-level abstractions: Atomic operations are building blocks for lock-free algorithms. For most application code, locks, concurrent collections, or channels are simpler and less error-prone.
Summary
- Atomic operations complete entirely or not at all — no thread sees a partial state
- The fundamental primitive is compare-and-swap (CAS), implemented in hardware
- Java:
AtomicInteger,AtomicLong,AtomicReference - C++:
std::atomic<T>with memory ordering options - Go:
sync/atomicpackage withAddInt64,LoadInt64,CompareAndSwapInt64 - Python: use
threading.Lock(no built-in atomic primitives outside the GIL) - Atomic operations are faster than locks but harder to reason about — prefer higher-level concurrency abstractions for application code

