Ответ 1
dosync
и synchronized
предоставляют доступ к совершенно другим абстракциям concurrency.
synchronized
- способ приобретения и освобождения замков. Когда поток входит в блок synchronized
, он пытается получить соответствующую блокировку; если блокировка в настоящее время поддерживается другим потоком, текущий поток блокирует и ждет его освобождения. Это приводит к определенным проблемам, таким как риск тупика. Блокировка освобождается, когда нить выходит из блока synchronized
.
dosync
обозначает блок кода, который должен выполняться в транзакции. Транзакции в Clojure - это способ координации изменений в Ref (объекты, созданные с помощью функции ref
); если вам нужен какой-то код для согласованного представления некоторых частей изменчивого состояния в Clojure - и, возможно, их изменение, вы помещаете их в Refs и выполняете свой код в транзакции.
У транзакции есть интересное свойство, которое она перезапустит, если по какой-либо причине она не сможет выполнить, до определенного максимального количества попыток (в настоящее время жестко запрограммировано на 10000). Среди возможных причин невозможности совершения транзакции - невозможность получить последовательное представление о мире (на самом деле, соответствующие Refs - существует объект "адаптивной истории", который делает это менее проблематичным, чем может показаться на Первый взгляд); одновременные изменения, сделанные другими транзакциями; и др.
Транзакция не рискует быть заторможенной (если программист не собирается вводить тупик, не связанный с системой STM через Java interop); livelock, с другой стороны, является определенной возможностью, хотя это не очень вероятно. В общем, многие - хотя и не все! - интуитивных программистов, связанных с транзакциями базы данных, действительны в контексте систем STM, в том числе для Clojure.
STM - это огромная тема; один отличный ресурс для изучения Clojure STM - это Mark Volkmann Software Transactional Memory. Он углубляется в обсуждение Clojure STM в своих заключительных разделах, но начало может служить отличным вводным чтением.
Что касается цитированного фрагмента, на самом деле это не то, что вы обычно хотели бы эмулировать в производственном коде, так как блоки dosync
должны почти всегда быть свободными от побочных эффектов; print
здесь может быть полезен для демонстрации внутренней работы STM, но если вы хотите, чтобы транзакция вызывала побочные эффекты в реальном коде, вы должны заставить ее создать агент Clojure для этой цели (который будет выполнять только его задача, если транзакция успешно завершится).