Race Condition: Lost Update on a Shared Counter

race-condition
Mechanism lab

Race Condition: Lost Update on a Shared Counter

Two actors read a shared counter, both increment, both write back — one update is lost. Fix with an atomic compare-and-set so a stale writer is refused and retries.

You'll predict the failure first, then write the fix and watch your own code drive the behavior.

Understand it step by step (한국어로 이해 → 영어로 말하기)

두 사람이 같은 숫자를 동시에 1씩 올린다. 그냥 두면 한 번 올린 게 사라진다. 그걸 막는 이야기.
  1. 1

    공유 숫자(카운터)가 하나 있다. A와 B 두 명이 각자 이 숫자를 1씩 올리려고 한다.

    class Counter {
        int value = 0;
        int read() { return value; }
    }
    🗣 영어로 말해

    We have one shared counter, and two actors each want to add one to it.

    checking microphone…

  2. 2

    올리는 방법: 먼저 지금 값을 '읽고', 거기에 1을 더해서 '다시 쓴다'. 읽기와 쓰기가 따로따로다.

    ⚖️ Trade-off: 읽기와 쓰기 사이에 틈이 생긴다. 그 사이에 남이 끼어들 수 있다.

    int seen = c.read();      // read
    int next = seen + 1;      // modify
    c.compareAndSet(seen, next); // write back
    🗣 영어로 말해

    Each one reads the current value, adds one, then writes it back.

    checking microphone…

  3. 3

    문제: A와 B가 둘 다 0을 읽는다. 둘 다 1을 계산한다. 둘 다 1을 쓴다. 결과는 2가 아니라 1.

    ⚖️ Trade-off: B의 쓰기가 A의 쓰기를 덮어버린다. 한 번 올린 게 조용히 사라진다 (잃어버린 업데이트).

    🔧 도구:check-then-actread-modify-write

    🗣 영어로 말해

    If both read zero, both write one, and one increment is silently lost.

    checking microphone…

  4. 4

    해결: 읽기와 쓰기를 하나로 묶는다. '쓸 때, 값이 아직 내가 읽은 그대로면 쓰고, 아니면 거절'한다.

    ✅ Fix: 이걸 compare-and-set(CAS)이라 한다. '내가 본 게 0이었으니, 아직 0일 때만 1로 바꿔줘'라고 한 번에 부탁한다.

    🔧 도구:compareAndSet (CAS)AtomicIntegerAtomicLong

    boolean compareAndSet(int expected, int next) {
        if (value != expected) return false; // stale writer refused
        value = next;                         // atomic commit
        return true;
    }
    🗣 영어로 말해

    I make read and write one atomic step: compare-and-set commits only if the value is unchanged.

    checking microphone…

  5. 5

    이제 A가 먼저 0→1 성공. B가 0→1 시도하지만 값은 이미 1이라 '내가 본 0이 아니네' → 거절당한다.

    ✅ Fix: 거절당한 B는 새 값(1)을 다시 읽고 1→2로 재시도한다. 그래서 둘 다 결국 반영된다.

    🔧 도구:retry loop

    void increment() {
        while (true) {
            int seen = read();
            if (compareAndSet(seen, seen + 1)) return; // refetch + retry on refusal
        }
    }
    🗣 영어로 말해

    The stale writer is refused, so it re-reads the fresh value and retries.

    checking microphone…

  6. 6

    대가: 사람이 많이 몰리면 거절·재시도가 반복된다. 계속 헛돌며(spin) CPU를 쓴다.

    ⚖️ Trade-off: 쓰기가 아주 많으면 재시도가 늘어 느려질 수 있다.

    ✅ Fix: 락(lock)을 쓰면 거절 대신 줄 세워 기다리게 한다. 하지만 조용히 값이 사라지는 것보다는 재시도가 낫다.

    🔧 도구:lock (대안)spin/retry

    🗣 영어로 말해

    The cost is contention — callers spin and retry under heavy writes — but that beats losing updates.

    checking microphone…

6단계 영어를 다 말하면 → 이 메커니즘 전체를 영어로 설명할 수 있게 된다.

Explain it in English

Say it out loud in your own words — hit these terms — then check against the gold answer.

Say it in your own words — but hit these: concurrency · race condition · compare and set · atomicity · java

checking microphone…