Contents

Programming Fundamentals › Concurrency & Async

Lock-Free Data Structures

Concurrent structures built from atomic operations instead of locks.

Also known as: lock-free, lock free data structures, non-blocking algorithm

A lock-free data structure is one that multiple threads can use concurrently without a lock, built instead from atomic operations — most importantly compare-and-swap (CAS), which atomically sets a value only if it still holds an expected old value. The defining property is progress: if one thread is delayed or paused, the others can still make progress. A lock can’t promise that; if the lock owner stalls, everyone waiting on the lock stalls too.

CAS(address, expected, new):
  if *address == expected: *address = new; return success
  else: return failure   (someone changed it first)

# update by retrying until your CAS succeeds
loop:
  old = load(counter)
  if CAS(counter, old, old + 1): break

That retry loop is the shape of most lock-free code: read, compute, attempt CAS, retry if someone beat you. Queues, stacks and counters are commonly implemented this way.

The classic mistakes:

  • Assuming lock-free means faster. It usually doesn’t in the common case — a correct lock-free structure is more complex, touches shared cache lines heavily, and can be slower than a simple lock under low contention. Its value is progress and predictability under contention, not raw speed.
  • The ABA problem. CAS checks a value, but a value can change from A to B and back to A between the read and the CAS; the CAS “succeeds” even though the world changed. Solutions use version tags or hazard pointers. This bug is subtle and dangerous.
  • Ignoring memory ordering. Atomicity isn’t the same as visibility. Without the right ordering, other threads may not see your writes when you think they should (see the memory model).
  • Memory reclamation is hard. Knowing when it’s safe to free a node another thread might still be reading is one of the hardest parts. Garbage-collected languages dodge it; manual ones use epoch or hazard-pointer schemes.
  • Reaching for it too early. For most code, a mutex is correct and clear. Lock-free is a specialist tool for hot paths and real contention.

Use lock-free structures via well-tested libraries, not hand-rolled attempts. Getting them right requires understanding atomics, ordering and reclamation together — which is exactly what makes them an advanced, error-prone area rather than a default choice.