Can we make coordination unnecessary instead of correct?

Immutability & Concurrency Control

The strategies that reduce synchronization rather than perfecting it: immutability, copying versus sharing, copy-on-write snapshots, and optimistic against pessimistic concurrency control with the contention threshold that decides between them.

Immutability as a Concurrency Strategy▶ lab

Mutable shared data is a synchronization problem. Immutable shared data is not a problem at all — any number of readers can touch it simultaneously with no lock, no ordering rule and no critical section. The strongest move in concurrency is removing the need for it.

Q · If the data can never change after construction, what is left to synchronize?

Copy or Share?

Copying buys isolation and pays in memory and copy time. Sharing buys efficiency and pays in synchronization. This is a genuine decision with a crossover point, not a style preference — and the crossover moves with payload size, task count and how much of the data each task actually touches.

Q · Should each task get its own copy of this data, or should they all point at one?

Copy-on-Write as a Concurrency Strategy

Readers share one stable version and pay nothing. A writer builds a new version off to the side and swaps the pointer. Nobody blocks anybody. The kernel uses the same idea for fork; a database uses it for MVCC; here it is a way to make a mutable structure behave like an immutable one.

Q · How do I let readers proceed without a lock while a writer is changing the same structure?

Optimistic Concurrency Control▶ lab

Read version 5, compute the change, write only if the row is still at version 5. If it moved, somebody else got there first — retry or fail. No locks held, no blocking, and a failure mode that arrives all at once: retry storms under high contention.

Q · Can I update shared state without holding a lock while I decide what to write?

Pessimistic Concurrency▶ lab

Take the lock, make the change, release the lock. Conflicts are prevented instead of detected, the loser waits instead of redoing work, and latency becomes predictable — which is exactly the right trade when contention is high or a conflict is expensive to undo.

Q · When is it right to make everyone else wait rather than let them race and retry?

Optimistic vs Pessimistic▶ lab

Four numbers decide it: how often two writers collide, how expensive a conflict is to resolve, how long the state must stay stable, and what a retry costs. Optimistic wins comfortably at low contention and degrades badly — not gradually — as contention rises. That curve is the lesson.

Q · At what contention level does letting writers race and retry become worse than making them queue?

Initialization Races▶ lab

"If the singleton is missing, create it" is a check-then-act, and two threads running it produce two singletons — or, far worse, one thread handing out a reference to an object the other thread has not finished building. Once-initialization primitives exist because this is genuinely hard to get right by hand.

Q · What happens when two threads both discover that the thing they need does not exist yet?