Work through practical Clojure concurrency examples that show Java engineers when to use atoms, refs, agents, queues, futures, and immutable snapshots in ordinary JVM applications.
Concurrency becomes useful when you can recognize the shape of the problem in ordinary application code. This section turns the earlier state tools into concrete examples: counters, coordinated application state, background work, and mixed concurrency designs.
Do the examples in a REPL and deliberately add contention. Java developers often learn the most when they can see which invariants remain stable without locks, which operations retry, and where Java concurrency utilities still belong at the boundary.
| Example | Main primitive | What to notice |
|---|---|---|
| Counter | Atom | One independent state value can be updated atomically with a pure function. |
| App State | Refs and STM | Multiple values can change together without manual lock ordering. |
| Background | Agent | Ordered asynchronous state updates are useful when immediate results are not required. |
| Combining | Several tools | Real systems often use values, references, and Java queues together behind clear ownership boundaries. |