SDE Path

Concurrency

Concurrency & Thread Safety for LLD

Race conditions, locks, atomics, and thread-safe designs — the concurrency an LLD round expects you to reason about.

13 min5 steps · 7 checks
Step 1 of 50/7 checks correct

A race condition appears the moment two threads touch the same mutable state and the final result depends on who happened to run first. The two shapes that trip up LLD candidates are read-modify-write (like count++) and check-then-act (like "if absent, put").

count++ reads as one keystroke but compiles to three separate steps: load the current value, add one, store it back. Two threads can each load the same starting value, each add one, and each store the same result — so two increments collapse into one. That is a lost update.

class Counter {
    private int count = 0;

    void increment() {
        count++; // load count, add 1, store count — three steps, not one
    }

    int get() { return count; }
}

Picture two threads calling increment() when count == 41:

Thread A: load count -> 41
Thread B: load count -> 41   (A has not stored yet)
Thread A: add 1     -> 42
Thread B: add 1     -> 42
Thread A: store     -> 42
Thread B: store     -> 42     (should be 43, one increment vanished)

Run 1000 increments across a few threads and you routinely land below 1000. The bug is not rare or exotic; it is the default behavior of unsynchronized shared mutable state. Check-then-act has the same disease: between your if (!map.containsKey(k)) and your map.put(k, v), another thread can slip in and the invariant you checked is already stale.

Quick check

Why can two threads calling count++ a total of 1000 times leave count below 1000?