Подтвердить что ты не робот

Clojure STM (dosync) x блок синхронизации Java

В чем разница между Clojure STM (dosync) и синхронизацией Java с блоком?

Я читаю код из проблемы "Спальный парикмахер". (http://www.bestinclass.dk/index.clj/2009/09/scala-vs-clojure-round-2-concurrency.html)

(defn the-shop [a]  
  (print "[k] entering shop" a)  
  (dosync     
    (if (< (count @queue) seats)  
      (alter queue conj a)  
      (print "[s] turning away customer" a))))

Чтобы избежать условий гонки, dosync используется, поэтому я спрашиваю себя: "Какая разница (STM) от блока синхронизации Java"? Будет ли он блокировать этот критический код?

Спасибо заранее! Dantas

4b9b3361

Ответ 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 для этой цели (который будет выполнять только его задача, если транзакция успешно завершится).

Ответ 2

Кроме превосходного ответа Michał, с транзакциями STM, вы всегда будете получать замороженное значение в начале транзакции и не должны ждать завершения текущей транзакции.

Ответ 3

Чтобы дать полную картину для тех, кто ищет, Clojure имеет аналог synchronized. Это полезно, когда нужно работать с Java не-потокобезопасными типами для взаимодействия.

(locking x & body)

Ответ 4

базовая разница следующая

Clojure STM поддерживает оптимистичный concurrency, тогда как синхронизированный JAVA пессимист

Clojure STM не получает блокировку, пока не будет более одного потока. если изменяемое состояние обновляется другим потоком, то операция внутри dosync повторяется. Кроме того, dosync является обязательным для изменяемых состояний. Clojure выдает исключение anticState, когда dosync отсутствует, в отличие от JAVA.