The full picture
🎬 Race condition on counter++ (and the fix)
1 / 8Two threads run counter++ concurrently. It looks atomic, but it compiles to three steps: read, add 1, write back.
The senior-level framing: a data race needs shared + mutable + unsynchronized state. Remove any one of the three and the race is gone. That ordering is also your design priority — the best concurrency bug is the one made impossible, not the one locked away.
| Fix | When | Trade-off |
|---|---|---|
| Immutability / confinement | Default choice — share nothing mutable | May require redesign; copies cost memory |
AtomicInteger / LongAdder | Single-variable counters, sequences | CAS retries under heavy contention; LongAdder fixes that at the cost of read accuracy |
synchronized / ReentrantLock | Multi-variable invariants (check-then-act) | Contention, possible deadlock, requires discipline |
| Concurrent collections | Shared maps/queues | Compound actions still need compute()-style atomic methods |