Lock-Free and Wait-Free Programming in Clojure: Advanced Concurrency Techniques

Learn what lock-free and wait-free really mean, where Clojure uses CAS-based lock-free techniques, and why most application code should prefer higher-level concurrency models.

Lock-free: A progress guarantee where some thread completes in a finite number of steps.
Wait-free: A stronger guarantee where every thread completes in a finite number of steps.

These terms are precise. They are not marketing labels for “fast concurrent code.” That precision matters because many articles overstate what ordinary application code can promise.

In Clojure, atoms rely on compare-and-swap and therefore use lock-free techniques for single-reference updates. But that does not mean all atom-based code is wait-free, and it definitely does not mean refs or agents are lock-free programming models in the same sense.

What Clojure Gives You Directly

The most direct lock-free building block in everyday Clojure is the atom:

1(def queue-depth (atom 0))
2
3(swap! queue-depth inc)

Under the hood, swap! retries with CAS when contention occurs. That gives you atomic single-reference updates without explicit locks, but not a per-thread completion guarantee. A hot atom can retry many times under contention.

Refs use STM. Agents use queued asynchronous execution. Both are valuable, but they solve different coordination problems and should not be described as wait-free application tools.

Why Wait-Free Is Rare in Application Code

Wait-free algorithms are hard to design, hard to verify, and usually reserved for specialized low-level structures. Most production systems do not need to hand-roll them. They need predictable correctness, good enough throughput, and clear ownership boundaries.

That is why most Clojure applications should choose among:

  • immutable values
  • atoms for one independent value
  • refs for coordinated transactions
  • agents for queued asynchronous updates
  • channels for explicit message passing

These tools solve the problem you probably have. Wait-free custom algorithms solve a much narrower class of problems.

When Lower-Level Lock-Free Design Makes Sense

Custom lock-free design is most justified when:

  • contention is the dominant bottleneck
  • allocation and synchronization overhead have been measured carefully
  • simpler coordination models have already failed to meet the target
  • the team can reason about correctness under real memory and retry semantics

Even then, the right implementation may come from proven JVM libraries rather than handwritten low-level code.

Practical Heuristics

Treat lock-free and wait-free as precise progress guarantees, not style words. In Clojure application code, the practical question is usually not “Can I build a wait-free queue?” but “Can I remove shared mutable coordination entirely?” Very often the answer is yes, and that answer is far cheaper to maintain.

Ready to Test Your Knowledge?

Loading quiz…
Revised on Thursday, April 23, 2026