How to implement an atomic counter
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
An atomic counter is a shared integer that multiple threads can update without using a mutex for each increment. The core idea is to use atomic read-modify-write operations provided by the language or platform so increments and decrements happen safely even when many threads race at the same time.
Why a Normal Integer Fails
This looks harmless:
But on a real machine that is not one indivisible action. It is roughly:
- load the current value
- add one
- store the new value
If two threads do that at once, one update can overwrite the other. That is the classic lost-update race.
C++ Example With std::atomic
In modern C++, the usual implementation uses std::atomic:
This counter is thread-safe for counting because each update is atomic.
Why memory_order_relaxed Is Often Enough
For a pure counter that only tracks a numeric total and does not publish other shared state, memory_order_relaxed is often the right choice. It guarantees atomicity of the counter itself without adding stronger ordering constraints than necessary.
If the counter also coordinates visibility of other data, you may need stronger ordering such as memory_order_acquire, memory_order_release, or the default sequential consistency. That depends on the full concurrency design, not only on the counter.
When a Mutex Is Still Better
Atomic counters are great for simple numeric state. They are not a universal replacement for locks.
If your update logic is:
- read several variables
- modify multiple related fields
- maintain a complex invariant
then an atomic counter alone is not enough. A mutex or another higher-level synchronization primitive is usually clearer and safer.
Fetch-Then-Use Patterns
Atomic operations also let you get the old value or new value in one step:
That is useful for generating unique IDs, ticket numbers, or sequence counters where each thread needs a distinct numeric result.
Example in Java
The same idea exists in other languages. In Java, AtomicInteger is the common tool:
Different languages expose different APIs, but the concurrency principle is the same.
Performance Expectations
Atomic counters are usually faster than taking a mutex for every increment under light or moderate contention. Under heavy contention, they can still become a bottleneck because many threads fight over the same cache line.
If you need very high update rates, techniques such as sharded counters or per-thread counters with periodic aggregation may scale better than one global atomic variable.
Common Pitfalls
The most common mistake is assuming "atomic" means "solves all concurrency problems". It solves atomic updates for one variable, not arbitrary multi-step invariants.
Another issue is using unnecessarily strong memory ordering without understanding why, which can hurt performance and confuse the design. Developers also sometimes assume a counter read combined with other shared state is safe just because the counter itself is atomic. That is only true if the surrounding synchronization rules are correct.
Summary
- Use language-provided atomic types such as
std::atomic<int>for a shared counter. - Atomic increment avoids lost updates that happen with ordinary integers.
- '
memory_order_relaxedis often enough for pure counting.' - Atomic counters do not replace mutexes for complex shared-state updates.
- Under high contention, consider sharded or aggregated counters for better scalability.
Related reading
- How to implement cancellation in Request Reply Pattern in .NET?
- How to implement .get feature with FutureTask or BackgroundTask using android?
- How to implement lock-free skip list
- How to implement multithread safe singleton in C11 without using mutex
- How to increase debezium / kafka connect performance for initial snapshot of millions of records and enable snapshot parallely if possible?
- How to increase thread priority in pthreads?
- How to initialize a Thread in Kotlin?
- How to interpret a Java thread stack?
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.