Even a simple shared integer can produce unpredictable results.
🧠 The Scenario
Let’s say we have a shared variable:
- One thread increments (+1)
- Another thread decrements (-1)
Expected result:
0
Actual result:
❌ Unpredictable
❗ What’s Going Wrong?
At first glance, this looks harmless:
value++;
But this is NOT atomic.
It actually involves three steps:
- Read
- Modify
- Write
Now imagine two threads executing this at the same time:
Thread A → Read (0)
Thread B → Read (0)
Thread A → Write (1)
Thread B → Write (-1)
Final result: -1 instead of 0
⚠️ Race Condition
This is a classic race condition:
- Multiple threads access shared data
- At least one modifies it
- No proper synchronization
Result:
- Inconsistent data
- Unpredictable behavior
- Hard-to-debug issues
💻 Java Example
class Counter {
int value = 0;
void increment() {
value++; // not atomic
}
void decrement() {
value--; // not atomic
}
}
public class Main {
public static void main(String[] args) throws Exception {
Counter counter = new Counter();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 10000; i++) counter.increment();
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 10000; i++) counter.decrement();
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(counter.value); // ❌ unpredictable
}
}
✅ Fixes
1. synchronized
- Easy to use
- Ensures mutual exclusion
- ❌ Can block threads
synchronized void increment() {
value++;
}
2. AtomicInteger
- Lock-free and efficient
- Ideal for counters
import java.util.concurrent.atomic.AtomicInteger;
AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();
3. Locks (ReentrantLock)
- More control
- Useful for complex cases
- ❌ More verbose
🌐 JavaScript Version (Async Race Condition)
Even though JavaScript is single-threaded, async operations can still create race conditions:
let counter = 0;
async function increment() {
let temp = counter;
await Promise.resolve();
counter = temp + 1;
}
async function decrement() {
let temp = counter;
await Promise.resolve();
counter = temp - 1;
}
🔐 JavaScript Fix (Mutex)
class Mutex {
constructor() {
this.locked = false;
this.queue = [];
}
lock() {
return new Promise(resolve => {
if (!this.locked) {
this.locked = true;
resolve();
} else {
this.queue.push(resolve);
}
});
}
unlock() {
if (this.queue.length > 0) {
const next = this.queue.shift();
next();
} else {
this.locked = false;
}
}
}
🎯 Key Takeaway
Concurrency bugs don’t fail loudly.
They fail silently.
And that’s what makes them dangerous.
🚀 Final Thoughts
This small example completely changed how I think about shared state.
If you’re working in backend or distributed systems, this is something you must understand deeply.
🏷️ Tags (add on DEV)
#java #javascript #concurrency #backend #programming
📌 About Me
Sharing what I learn as I grow.
#CodeWithIshwar
Top comments (0)