Refs and STM
STM (Software Transactional Memory) gives ACI (atomic, consistent, isolated) transactions using multiversion concurrency control. Don't perform side effects inside dosync (it may retry). Use agents (sends are held until the transaction commits) for effects.
In practice, a single atom holding a map covers most needs; refs shine when several independent identities must change together.
(def account-a (ref 100))
(def account-b (ref 0))
(defn transfer! [from to amount]
(dosync ; a transaction: all or nothing, isolated, retried on conflict
(when (< @from amount)
(throw (ex-info "Insufficient funds" {:balance @from})))
(alter from - amount)
(alter to + amount)))
(transfer! account-a account-b 30)
[@account-a @account-b] ; [70 30]
;; commute: for commutative updates (less contention; order doesn't matter)
(def hits (ref 0))
(dosync (commute hits inc))
;; ensure: protect a ref you read but don't write from concurrent modification (write skew)
(dosync (ensure account-a) (alter account-b + 1))
;; Parallel stress test
(def total (ref 0))
(dorun (pmap (fn [_] (dosync (alter total inc))) (range 1000)))
@total ; 1000