Concurrency and Parallelism in Clojure

Atoms, refs, agents, STM, reducers, futures, and channels for safe state coordination and parallel work in Clojure.

This section covers the part of Clojure that most clearly distinguishes it from many other JVM languages: explicit, well-separated concurrency models. Instead of one generic shared-memory story, Clojure gives you several tools for different coordination problems, from atomic updates to coordinated transactions and asynchronous pipelines.

The goal here is to learn not just how these primitives work, but when each one is the right fit and what kinds of bugs or performance problems show up when they are mixed carelessly.

The quickest way to read this chapter well is to separate the problems before you separate the code:

  • use atoms when one value changes independently
  • use refs when several values must preserve an invariant together
  • use agents when updates should happen asynchronously
  • use futures and reducers for parallel computation
  • use channels when ownership and message flow are the main story

That framing matters more than memorizing APIs. Most Clojure concurrency mistakes come from choosing the wrong coordination model first and trying to tune the consequences later.

In this section

Revised on Thursday, April 23, 2026