Why Concurrency Is Hard
Writing code that runs on a single CPU is straightforward: instructions execute one after another, in order, with predictable results. Writing code that runs across 64 CPU cores simultaneously — all sharing the same memory — is a fundamentally different problem. The difficulty isn't the parallelism itself; it's coordinating access to shared state without breaking correctness or destroying the performance gains you were trying to achieve in the first place.
Think of it like a shared whiteboard in an office. One person writing on it is fine. Two people writing on it simultaneously produces gibberish. The obvious fix — only one person can hold the marker at a time — works, but now 63 people are standing in line waiting for the marker. You've solved the correctness problem and created a performance problem.
The Two Enemies:
- Race Conditions (Safety Failure): Two threads read, modify, and write the same variable concurrently. Because neither thread sees the other's in-progress changes, one thread's update silently overwrites the other's. The data is now corrupted with no error thrown.
-
Fix: Locking — force threads to take turns.
-
Contention (Performance Failure): You added locks everywhere. Now your 64 CPUs are forming a single-file queue to access a shared variable. You've serialized a parallel program. Throughput collapses.
- Fix: Minimize lock scope, use finer-grained locks, or redesign to reduce sharing.
The Goal:
Maximize concurrency — threads running independently and making progress — while minimizing contention — threads blocking each other waiting for locks.
Interviewers often ask: "What's the difference between a race condition and a deadlock?" A race condition is a safety failure — threads interfere and corrupt data. A deadlock is a liveness failure — threads are all waiting and no one makes progress. Both are concurrency bugs, but they have different causes and different fixes. Knowing this distinction cold signals genuine understanding.
Key Point: The fundamental challenge of concurrent programming is not writing fast code — it's writing correct code that is also fast. Correctness comes first. Once you have a correct, locked implementation, you optimize by reducing lock scope and contention. Never optimize first.