开发者

Clojure STM ( dosync ) x Java synchronize block

What is the difference between Clojure STM (dosync) approach and Java synchronize Block?

I'm reading the code below from "The sleeping barber" prob开发者_开发知识库lem. (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))))

To avoid race conditions, dosync is used, so i ask myself "What is the difference (STM) from Java synchronize block" ? Will it block this critical code ?

Thanks in advance ! Dantas


dosync and synchronized give access to completely different concurrency abstractions.

synchronized is a way of acquiring and releasing locks. When a thread enters a synchronized block, it attempts to acquire the appropriate lock; if the lock is currently held by a different thread, the current thread blocks and waits for it to be released. This leads to certain problems, such as the risk of deadlock. The lock is released when the thread leaves the synchronized block.

dosync marks a block of code which is to be run in a transaction. Transactions in Clojure are a way of coordinating changes to Refs (objects created with the ref function); if you need some code to have a consistent view of some pieces of mutable state in Clojure -- and possibly change them -- you put those in Refs and execute your code in a transaction.

A transaction has the interesting property that it will restart if for some reason it cannot commit, up to a certain maximal number of retries (currently hard-coded to be 10000). Among the possible reasons for a transaction being unable to commit are an inability to obtain a consistent view of the world (actually, the relevant Refs -- there is an "adaptive history" facility which makes this less of a problem than it might seem at first glance); simultaneous changes made by other transactions; etc.

A transaction runs no risk of being deadlocked (unless the programmer goes out of their way to introduce a deadlock unrelated to the STM system through Java interop); livelock, on the other hand, is a certain possibility, though it is not very probable. In general, many -- although not all! -- of the intuitions programmers associate with database transactions are valid in the context of STM systems, including that of Clojure.

STM is a huge topic; one excellent resource for learning about Clojure's STM is Mark Volkmann's Software Transactional Memory article. It goes into great depth in discussing Clojure's STM in its final sections, but the beginning can serve as great introductory reading.

As for the snippet you quoted, it's actually not something you would normally want to emulate in production code, since dosync blocks should almost always be side-effect free; the print here can be useful for demonstrating the inner working of the STM, but if you wanted a transaction to cause side-effects in real code, you should have it spawn a Clojure Agent for the purpose (which would only execute its task if the transaction successfully commits).


Also in addition to Michał's excellent answer, with STM transactions, reads always get you the frozen value at the beginning of the transaction and need not wait for any ongoing transaction to complete.


Just to give a full picture for those seeking, Clojure does have a synchronized analog. It is useful when one has to work with Java non-threadsafe types for interop.

(locking x & body)


basic difference is following

Clojure STM supports optimistic concurrency whereas JAVA synchronized is pessimist

Clojure STM does not acquire lock until there are more than one thread. if mutable state is updated by another thread then the operation inside dosync is repeated. Also, dosync is mandatory for mutable states. Clojure throws illegalState exception when dosync is missing, unlike JAVA.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜