Welcome to Pillar #3 of the Concurrency & Multithreading: The Ultimate Engineer’s Bible series.
“Two threads walk into a bar. One increments a counter. The other resets it. The bar burns down. Nobody knows why.” — Ancient concurrency proverb.

Visibility isn’t enough. You can see the data — great. But what if two threads update it at the same time?
That’s the real war: race conditions.
Atomicity means:
An operation is indivisible. It either happens fully or not at all.
No other thread can peek in while it’s halfway done. You need atomicity to ensure correctness.
✅ Read for free: Atomicity: Your Final Defense Against Race Conditions
If it helped you, clap once. If it helped a lot, clap more. That’s how it reaches others too.

🧠 The Classic Problem: count++
Let’s break down what seems like a harmless line:
count++;
This is not atomic. It expands to:
- Read count
- Add 1
- Write back
Now imagine two threads doing this simultaneously.
They both read count = 5, both compute 6, both write 6.
Expected: 7
Reality: 6
Boom. Race condition.
🛡️ Solution 1: Lock Everything
synchronized(lock) {
count++;
}
Sure, this works. But you’re back to blocking, locking, and slowing everything down.
Can we do better?
⚙️ Solution 2: Use Atomic Classes
Java gives you lock-free, thread-safe primitives with built-in atomicity.
Meet your new weapons:
- AtomicInteger
- AtomicLong
- AtomicBoolean
- AtomicReference
Example
AtomicInteger count = new AtomicInteger(0);
count.incrementAndGet(); // Atomic. Thread-safe.
You get visibility, atomicity, and performance. No explicit locks.
🧬 Behind the Scenes: CAS (Compare-And-Swap)
At the core of all this is a CPU-level operation called CAS.
It works like this:
“Hey memory, if the value is still 5, change it to 6.
If someone already changed it, I’ll retry.”
It’s a loop that keeps retrying until it succeeds.
This is what powers compareAndSet():
AtomicInteger count = new AtomicInteger(5);
boolean updated = count.compareAndSet(5, 6);
If the value is still 5, it gets updated to 6. If not, the thread can retry.
No locks. No blocking. Just raw CPU-backed atomic ops.
🧘 Analogy: The Post-It Note on a Fridge
Think of a shared fridge with a post-it note saying “Milk left: 1 bottle.”
Thread A reads the note and goes to add 1 bottle.
Thread B reads the same note and also adds 1 bottle.
They both update the note to “2”. But there’s actually 3 now.
With CAS:
Thread A says:
“If it still says 1, change it to 2.” ✅ Success
Thread B says:
“If it still says 1, change it to 2.” ❌ Fail — someone changed it
Thread B retries:
“Now it’s 2 — change it to 3.” ✅ Success
Everyone wins. That’s atomicity with CAS.
🧪 Useful Atomic Methods
These are gold in any multithreaded context:
- get() — Fetch the current value
- set(x) — Overwrite value
- incrementAndGet() — Atomically add 1 and return
- compareAndSet(expected, update) — CAS magic
These don’t need synchronized, and they’re extremely performant under high contention.
⚙️ LongAdder and DoubleAccumulator
When you have massive thread counts, even AtomicInteger can become a bottleneck due to CAS retry loops.
Use LongAdder or DoubleAccumulator:
LongAdder adder = new LongAdder();
adder.increment(); // Very fast under high concurrency
Instead of one counter, they use multiple cells — reducing contention.
You only get the final value when you call sum().
🧨 sun.misc.Unsafe — The Forbidden Knowledge
Deep inside Java, there’s a class called Unsafe. It lets you:
- Allocate memory manually
- Manipulate memory addresses
- Perform CAS directly
It’s what backs all the atomic classes.
You should never use it directly unless you’re building concurrency primitives. But knowing it exists? That’s next-level awareness.
👣 Common Pitfalls
- Compound operations (if (x == 5) x++) are still dangerous — use compareAndSet.
- Don’t mix Atomic* types with normal int — you lose the atomicity.
- CAS can fail repeatedly under heavy contention — beware the spin loop.
🔥 Recap: What You Must Remember
- count++ is not atomic
- synchronized works, but blocks
- Atomic* classes give you lock-free atomicity
- compareAndSet() is the key to concurrency without locks
- Use LongAdder for high-performance counting
- Know that Unsafe exists — but don’t use it lightly
🛤️ What’s Next?
You’ve mastered atomicity — the backbone of any correct concurrent system.
🔜 Pillar #4: Coordination — where we make threads work with each other, not against.
🧭 Series Navigation
- 🔝 Parent Blog: The Ultimate Concurrency & Multithreading Guide
- ⬅️ Previous: Visibility
- ➡️ Next: Coordination
🛠️ Show Your Support
If this post brought you clarity, saved you hours of Googling, or challenged the way you think:
- 🔁 Share it with a fellow engineer or curious mind.
- 💬 Comment with questions, feedback, or requests — I read every one.
- 📩 Request a topic you’d like covered next.
- ⭐ Follow to stay ahead as new deep-dive posts drop.
Pingback: Coordination: Making Threads Work Together, Not Collide - ByteByteNews