Concurrency FundamentalsRace Conditions & Critical SectionsMedium⏱️ ~2 min

Common Race Condition Patterns

Key Insight
Most race conditions follow a few common patterns. Recognizing them helps you spot bugs before they happen.
Common Race Condition Patterns
Check-Then-Actif (condition) {// gap here!act() }Read-Modify-Writeval = read()// gap here!write(val+1)Compound Opsif (!contains(k))// gap here!put(k, v)

Pattern 1: Check-Then-Act

You check a condition, then act on it. But the condition might change between check and act.

if (file.exists()) {
  // File deleted here!
  file.read(); // CRASH
}

Pattern 2: Read-Modify-Write

You read a value, modify it, write it back. Another thread does the same between your read and write.

counter++ // Read, Add, Write
// Both read 5, both write 6

Pattern 3: Compound Operations

Multiple operations that should be atomic but are not. HashMap put-if-absent is a classic example.

Pattern 4: Lazy Initialization

Creating a singleton on first use. Two threads might both see null and create two instances.

Prevention: Use atomic operations, proper locking, or thread-safe data structures. The fix depends on the pattern.
💡 Key Takeaways
Check then act: condition may change between check and action. Use atomic operations or hold lock during both.
Read modify write: interleaving causes lost updates. Use atomic operations (compareAndSwap) or locks.
Compound actions: multiple operations must complete atomically. Wrap entire sequence in a lock.
Lazy initialization: multiple threads may create duplicate instances. Use double checked locking or static initialization.
Time of check to time of use (TOCTOU): file system races where file state changes between check and use.
📌 Examples
1Lazy singleton: if (instance == null) { instance = new Singleton(); }. Two threads can both see null and create two instances.
2putIfAbsent: if (!map.containsKey(k)) { map.put(k, v); }. Another thread may insert between check and put.
← Back to Race Conditions & Critical Sections Overview