Data Race and Synchronization

A data race appears when multiple threads update shared state without coordination, and synchronization primitives prevent those lost updates.

Concurrency bugs are often hidden inside one innocent-looking line of code.

The statement count = count + 1 feels atomic at the source level, but the runtime performs it as separate read, compute, and write steps. In shared memory, those substeps can interleave unless a synchronization primitive prevents it.

When to reach for this

Reach for this concept when multiple threads read and write the same variable or when a program behaves nondeterministically under load.

Why this matters

Data races are some of the hardest bugs to reproduce because timing changes the result. Synchronization exists to turn timing-sensitive behavior back into deterministic behavior.

The mental model

Races are about interleaving

A race appears when one thread observes or overwrites state in the middle of another thread's update sequence.

Synchronization protects critical sections

Locks and similar primitives make a vulnerable read-modify-write sequence behave as one serialized unit.

Step through the concept

How to use this page

Follow the animation one state at a time and connect the code to the runtime behavior.

  • Run the race tab first and watch both threads read the same stale value.
  • Then run the lock tab and notice how waiting is what restores correctness.
  • Focus on the read-modify-write sequence because that is the real unit that must be protected.
Shared memoryLost update in action
Shared count starts at 0Two threads each increment once
Thread 1
Ready to read count
Local copy
Shared count
0
Lock state
No lock
Thread 2
Ready to read count
Local copy
Step 1 of 5
What
Both threads are about to execute count = count + 1 against the same memory cell.
Why
That statement is not atomic. It is really read, compute, then write. Interleaving those substeps is where races come from.
Critical section
// Thread 1 and Thread 2 both run this
count = count + 1

The shared counter behaves differently only because the interleaving changes

AspectUnsynchronizedSynchronized
Access patternThreads interleave freelyCritical section is serialized
CorrectnessLost updates possibleEach increment preserved
Main tradeoffMore parallelism, less safetyMore safety, less concurrency in the critical section

The short version

  • count = count + 1 is not atomic unless the language or hardware makes it atomic.
  • Data races happen because threads interleave the read, compute, and write steps.
  • Synchronization protects the critical section by forcing a safe order.
  • Correct concurrent code is mostly about controlling timing and shared access.