mutex
concurrency
multithreading
synchronization
parallel programming

How do mutexes really work?

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

A mutex is a coordination tool that lets only one thread enter a critical section at a time. At a high level that sounds simple, but the real implementation combines atomic CPU instructions, memory-ordering guarantees, and some form of waiting or scheduling so that threads do not corrupt shared state.

The Core Idea

Imagine several threads updating the same counter. Without synchronization, two threads can read the same old value, both compute a new value, and one update gets lost. A mutex prevents that by turning the shared section into a one-thread-at-a-time region.

Typical usage in C++ looks like this:

cpp
1#include <iostream>
2#include <mutex>
3#include <thread>
4#include <vector>
5
6std::mutex m;
7int counter = 0;
8
9void worker() {
10    for (int i = 0; i < 10000; ++i) {
11        std::lock_guard<std::mutex> lock(m);
12        ++counter;
13    }
14}
15
16int main() {
17    std::vector<std::thread> threads;
18    for (int i = 0; i < 4; ++i) {
19        threads.emplace_back(worker);
20    }
21    for (auto& t : threads) {
22        t.join();
23    }
24    std::cout << counter << "\n";
25}

The lock protects the increment so only one thread performs it at a time.

What Happens When a Thread Locks a Mutex

Conceptually, a mutex has two states:

  • unlocked
  • locked

The fast path usually works like this:

  1. the thread performs an atomic compare-and-set or exchange instruction
  2. if the mutex was unlocked, the thread becomes the owner
  3. if it was already locked, the thread must wait

That first step is important. A normal load and store would not be safe because two threads could observe the unlocked state at nearly the same time. Atomic instructions let the hardware decide one winner.

Why Mutexes Need Memory Ordering

Mutexes do more than serialize entry. They also provide visibility guarantees. When thread A unlocks a mutex after updating shared data, and thread B later locks the same mutex, thread B must see those updates in the correct order.

That is why lock and unlock are tied to acquire and release semantics. The mutex is not just a boolean flag. It is also a boundary that tells the CPU and compiler how memory operations may be reordered.

Without that guarantee, a program could still be wrong even if only one thread entered the critical section at a time.

What Happens on Contention

If a thread cannot acquire the mutex immediately, there are several possible implementation strategies.

A simple one is spinning:

  • repeatedly check whether the lock became free
  • consume CPU while waiting

That can be acceptable for extremely short wait times on multicore systems, but it wastes CPU if the owner holds the lock for longer.

Many operating systems use a hybrid approach:

  • try a fast user-space atomic path first
  • if contention continues, block the thread through the kernel
  • wake a waiting thread when the mutex becomes available

On Linux, this is often associated with futex-style behavior. On other systems the details differ, but the idea is similar: avoid kernel overhead when the lock is uncontended, involve the scheduler when waiting becomes real.

Ownership and Unlocking

A real mutex is owned by the thread that locked it. That ownership model matters because unlocking from the wrong thread is usually an error. This is one reason mutexes are different from lower-level primitives such as plain atomic flags.

Ownership also supports higher-level features such as:

  • debugging checks
  • recursion detection in special mutex types
  • deadlock diagnostics in tools

For ordinary mutexes, the expected rule is strict: the thread that locks must be the thread that unlocks.

Deadlocks and Lock Ordering

Mutexes solve data races, but they can create deadlocks if used carelessly. The classic case is two threads acquiring the same pair of locks in opposite order.

cpp
std::mutex a;
std::mutex b;

If thread one locks a then waits for b, while thread two locks b then waits for a, neither thread can proceed.

That is why teams establish lock-order rules or use helpers such as std::scoped_lock to acquire multiple mutexes safely.

Mutexes Are Not Magic Performance Tools

Mutexes make correctness possible, but they also serialize work. If a critical section is large, every other thread waits longer. If contention is constant, the mutex can become a bottleneck.

Good concurrent design therefore tries to:

  • keep critical sections short
  • protect only the shared data that truly needs protection
  • use atomics or read-write locks when they better match the access pattern

The correct question is not "can I add a mutex," but "what exact shared invariant am I protecting."

Common Pitfalls

  • Treating a mutex as just a boolean lock flag and forgetting that ownership and memory visibility are part of its contract.
  • Holding the mutex across slow work such as file I/O or network calls, which creates unnecessary contention.
  • Forgetting consistent lock ordering when multiple mutexes are involved, which leads to deadlocks.
  • Unlocking manually in code paths that can throw or return early. RAII wrappers such as std::lock_guard prevent this class of bug.
  • Using a mutex where a simpler atomic operation would be enough, increasing contention without a correctness benefit.

Summary

  • A mutex lets one thread at a time enter a critical section.
  • Real mutexes rely on atomic operations plus memory-ordering guarantees.
  • Contended locks usually transition from a fast user-space path to a blocking scheduler-assisted path.
  • Mutexes prevent data races, but careless use can create deadlocks and bottlenecks.
  • The best mutex usage protects a small, clearly defined shared invariant.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.