AlgoViz
Operating Systems
Operating Systems · MediumLesson 6 of 12

Race conditions

When two threads share memory, they can both try to change the SAME value at the SAME moment. When that happens, the result can come out plain wrong — not crash-wrong, just quietly, silently wrong. This is called a race condition, because the two threads are 'racing' to touch the same thing, and who gets there first changes the answer. Worst of all, it usually works fine… until one unlucky time it doesn't.

Two people, one shopping list

Imagine you and a roommate share one shopping list, and you each have your own copy on your phone. The list says '5 items'. You both want to add one thing. You read the list: 5 items. Your roommate reads it too: 5 items. You add 'milk' and write '6 items'. Your roommate adds 'eggs' and writes '6 items'. You both added something — that should be 7 items! — but the list says 6. One of your additions got erased, because you both started from the old number 5 and didn't see each other's change. That's a race condition. Nobody made a mistake on purpose. The problem is that two people changed the same thing based on the same out-of-date starting point.

The lost update, step by step

🧮Counter = 5reads 5reads 5Thread Aread 5, +1 → 6Thread Bread 5, +1 → 6writes 6writes 6Counter = 6 (should be 7!)
Both threads read 5, both add 1, both write 6. Two increments happened, but the counter only went up by ONE — a whole +1 is lost.

Let's do it with a real counter that starts at 5. Two threads, A and B, both want to add 1. You'd expect 5 + 1 + 1 = 7. Watch what actually happens when they overlap: • Thread A reads the counter: it sees 5. • Thread B reads the counter: it ALSO sees 5 (A hasn't written its answer back yet). • Thread A computes 5 + 1 = 6, and writes 6. • Thread B computes 5 + 1 = 6 (it's still working from the 5 it read), and writes 6. Final answer: 6. Two threads each added 1, but the counter only climbed by one. One whole increment vanished into thin air. This is the famous 'lost update', and it's the heart of every race condition.

Why it happens: read-modify-write isn't instant

The sneaky part is that 'add 1 to the counter' looks like ONE action, but it's really THREE tiny steps: read the value, add one to it, write it back. We call this a read-modify-write. Because it's three steps and not one, there's a gap between them. The OS can pause Thread A right after it reads 5 — before it writes 6 — and let Thread B run. B reads the same old 5, and now they're both doomed to write 6. The second thread sneaked in during the gap, while the first was in the middle of its update. If 'add 1' truly happened all at once, with no gap anyone could slip into, there'd be no race. The whole problem is that gap.

Why this is so nasty to catch

A race condition usually doesn't show up. Most of the time, Thread A finishes its three little steps before Thread B even starts, so you get the right answer 7 and everything looks perfect. The bug only appears on the rare run where the two threads overlap in just the wrong way. That means it can pass every test on your computer, then fail once a day on a busy server, with no clue why. The same code gives different answers depending on invisible timing you don't control. That's what makes race conditions one of the trickiest kinds of bug — and why we don't try to 'get lucky' with timing. We fix them on purpose, with a tool called a lock, which is the very next topic.

Questions you might have

Wait, why did the second thread read 5 instead of 6?

Because the first thread hadn't WRITTEN its 6 back yet. The first thread read 5 and was still in the middle of its work (it had the 6 in its head but not on paper). During that gap, the second thread looked at the counter and still saw the old 5. They both started from 5, so they both end at 6.

Does this happen every single time two threads run?

No — and that's exactly what makes it dangerous. Most of the time one thread finishes before the other starts, and you get the right answer. The bug only appears on the unlucky runs where they overlap at just the wrong moment. So the code can look totally fine for ages and then go wrong once in a while.

Is the computer making a mistake in the math?

No, the math is perfect every step. 5 + 1 really is 6. The trouble is that BOTH threads did '5 + 1' because both started from the same old 5. The wrong answer comes from the timing of who read and wrote when — not from any bad arithmetic.

Can this happen with processes too, or only threads?

Mostly threads, because threads share memory by default, so they're constantly touching the same values. Separate processes have their own private memory, so they don't collide as easily — but if they share something (like the same file or a shared chunk of memory), they can race too. The rule is: whenever two things change one shared thing at once, you can get a race.

Okay, so how do we fix it?

You make sure only ONE thread can touch the shared value at a time — so the second one has to wait until the first has fully finished its read-add-write. The tool that does this is called a lock (or mutex), and it's the very next topic.

🧠A race condition is when two threads change the same value at the same time and the result comes out wrong. The classic case is the 'lost update': both read 5, both write 6, so a whole +1 disappears. It happens because read-modify-write isn't instant — there's a gap a second thread can sneak into. The fix is locks.
✅ Check yourself3 quick questions — prove the idea stuck.Start →🧭 Take it furtherA new system you haven't read about — would you reach for this idea, and what does it cost?Try it →▶ Now watch it moveOpen the animation →

Best read after: What is a thread?