Question

What are mutexes, semaphores and atomic operations?

Vault Verified
Curated Intelligence
Definitive Source
Answer

Three mechanisms for coordinating concurrent access to shared state, at different levels of abstraction — and choosing the wrong one is how concurrency bugs are introduced.

Atomic operations. A single operation on a variable that is indivisible — no other thread can observe it half-completed. Implemented in hardware, typically as compare-and-swap.

Why they matter: counter++ is not atomic. It reads, adds and writes, and two threads interleaving those steps lose an increment. An atomic increment is one indivisible operation and cannot.

Use them for: single-variable operations — counters, flags, simple state transitions. They are the fastest option because they involve no locking, and their limit is that they protect one variable, not an invariant across several.

Mutexes (mutual exclusion locks). Ensure that only one thread at a time executes a region of code. A thread acquires the lock, does the work, releases it; others wait.

Use them for: protecting a group of related variables that must stay consistent together, which atomics cannot do.

Their costs: contention — waiting threads do no work; deadlock, when locks are acquired in inconsistent orders; priority inversion; and the fact that holding a lock while doing slow work (I/O especially) serialises your program.

Semaphores. A counter controlling access to a pool of resources. A thread decrements to acquire and increments to release, blocking at zero.

Use them for: limiting concurrency — at most N simultaneous database connections, downloads or worker tasks. A binary semaphore resembles a mutex but differs importantly: a mutex has an owner and must be released by the acquiring thread; a semaphore does not, which makes it usable for signalling between threads.

The practical guidance:

Prefer not sharing state at all — message passing, immutability and thread-local data remove the problem rather than managing it.

Use the highest-level construct available, since concurrent collections and channels are easier to get right.

Hold locks briefly, never across I/O, and acquire in a consistent global order.

Beware memory visibility, which is a separate problem from mutual exclusion.

Related Questions