What is a race condition
A race condition occurs when two or more threads or processes access shared data concurrently and the final outcome depends on the unpredictable order of execution. It is a timing bug that can cause incorrect results, crashes, or security flaws.
Computer Science · Concurrency
A race condition is a concurrency defect that appears when multiple execution contexts read and write the same memory location without proper coordination. The program’s state becomes dependent on the exact timing of each context, which the operating system does not guarantee. Because the scheduler may interleave operations in many ways, the same input can produce different outputs on different runs.
Why race conditions happen
Modern CPUs execute instructions out of order and operating systems preempt threads at arbitrary points. When a shared variable is accessed, each thread typically performs a read, modifies the value in a register, then writes it back. If another thread reads the original value before the first thread writes its result, the modification is lost. This lack of atomicity in the read‑modify‑write sequence is the root cause of most race conditions.
Common symptoms of a race condition include:
- Intermittent crashes or hangs
- Incorrect calculation results that appear only under load
- Security breaches such as double‑spending or privilege escalation
Steps to reproduce a minimal race condition:
- 1Create two threads that both increment a shared integer counter
- 2Initialize the counter to 0
- 3Start the threads simultaneously and let each perform 1,000 increments
- 4Join the threads and print the final counter value
Consider a concrete example: a global variable counter starts at 0. Thread A reads counter (0), adds 1, and pauses before writing. Thread B reads the same counter value (0), adds 1, and writes 1. When Thread A resumes, it also writes 1, overwriting B’s update. After both threads finish, the counter is 1 instead of the expected 2, demonstrating a lost update caused by a race condition.
Possible interleavings and final counter values:
| Interleaving | Final value |
|---|---|
| A reads, B reads, A writes, B writes | 1 |
| A reads, A writes, B reads, B writes | 2 |
| B reads, B writes, A reads, A writes | 2 |
Preventing race conditions requires enforcing exclusive access or using atomic primitives. A mutex lock forces only one thread to execute the critical section at a time, guaranteeing a consistent result. Lock‑free techniques such as compare‑and‑swap or language‑provided atomic types also serialize updates without a heavyweight lock. Always identify shared mutable state and protect it with the appropriate synchronization mechanism before testing under concurrency.
Check yourself
If two threads each increment a shared counter from 0 to 1 without synchronization, what final value can appear?
Get this as a lesson built for you
Describe what you are studying and Lernex writes the lesson and the questions around it. Free, and it takes about a minute.
Try itNo account needed to try it.
What people ask next
- How do mutexes work in multithreaded programs?Ask
- What is the difference between a lock and a semaphore?
- When should I use atomic variables instead of locks?Ask
- why is binary search O(log n)
- why are hash table lookups O(1)
- what is the difference between a stack and a queue
- why does 0.1 + 0.2 not equal 0.3
