Предположим, у вас есть несколько банковских счетов:
(def accounts
[(ref 0)
(ref 10)
(ref 20)
(ref 30)])
И атомная «передаточная» функция:
(defn transfer [src-account dest-account amount]
(dosync
(alter dest-account + amount)
(alter src-account - amount)))
Который работает следующим образом:
(transfer (accounts 1) (accounts 0) 5)
(map deref accounts)
=> (5 5 20 30)
Затем вы можете легко составить функцию переноса, чтобы создать транзакцию более высокого уровня, например, перенос с нескольких учетных записей:
(defn transfer-from-all [src-accounts dest-account amount]
(dosync
(doseq [src src-accounts]
(transfer src dest-account amount))))
(transfer-from-all
[(accounts 0) (accounts 1) (accounts 2)]
(accounts 3)
5)
(map deref accounts)
=> (0 0 15 45)
Обратите внимание, что все множественные переносы происходили в одной объединенной транзакции, то есть было возможно «составить» меньшие транзакции.
Сделать это с блокировками очень быстро будет сложно: если учетные записи необходимо заблокировать по отдельности, вам нужно будет сделать что-то вроде установления протокола по порядку получения блокировки, чтобы избежать взаимных блокировок. Сделать ошибку, которую трудно обнаружить, очень легко. СТМ спасает вас от всей этой боли.