Browse Clojure Foundations for Java Developers

The Challenges of Concurrency

Why shared mutable state is hard on the JVM and how immutability changes the game.

Concurrency is difficult in every JVM language because the hard part is not syntax—it is coordination. Once multiple threads can observe and change the same “thing,” you get failure modes that are rare in single-threaded code:

  • race conditions (“sometimes it’s wrong”)
  • visibility issues (“one thread didn’t see the update”)
  • deadlocks and lock ordering problems
  • lost updates and broken invariants

Clojure’s default of immutable data removes a huge class of problems: values can be shared freely across threads because they do not change. But you still need state somewhere, and state must be managed explicitly.

This chapter teaches how Clojure models state with a small set of tools (atoms, refs/STM, agents, and channels), and how to choose the one that matches the kind of coordination you actually need.

In this section

Revised on Friday, April 24, 2026