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
,所以我问我自己“与 Java 同步块有什么区别(STM)”?它会阻止这个关键代码吗?
提前致谢 ! 丹塔斯
What is the difference between Clojure STM (dosync) approach and Java synchronize Block?
I'm reading the code below from "The sleeping barber" problem.
(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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
dosync
和synchronized
提供对完全不同的并发抽象的访问。synchronized
是一种获取和释放锁的方式。当线程进入同步块时,它会尝试获取适当的锁;如果锁当前由不同的线程持有,则当前线程将阻塞并等待它被释放。这会导致某些问题,例如死锁的风险。当线程离开synchronized
块时,锁被释放。dosync
标记要在事务中运行的代码块。 Clojure 中的事务是协调 Refs(使用 ref 函数创建的对象)更改的一种方式;如果您需要一些代码来对 Clojure 中的某些可变状态保持一致的视图(并可能更改它们),您可以将它们放入 Refs 中并在事务中执行代码。事务有一个有趣的属性,如果由于某种原因无法提交,它将重新启动,直到达到一定的最大重试次数(当前硬编码为 10000)。事务无法提交的可能原因之一是无法获得一致的世界观(实际上,相关参考资料 - 有一个“自适应历史记录”设施,这使得这不像看起来那样是一个问题第一眼);其他交易同时进行的更改; 事务不会有死锁的风险(除非
程序员不遗余力地通过 Java 互操作引入与 STM 系统无关的死锁);另一方面,活锁是有一定可能性的,尽管可能性不大。一般来说,很多——尽管不是全部! -- 程序员与数据库事务相关的直觉在 STM 系统(包括 Clojure)的上下文中是有效的。
STM是一个很大的话题; Mark Volkmann 的软件事务内存文章是了解 Clojure STM 的绝佳资源。它在最后几节深入讨论了 Clojure 的 STM,但开头部分可以作为很好的入门读物。
至于您引用的代码片段,实际上您通常不想在生产代码中模拟它,因为 dosync 块几乎总是没有副作用;这里的 print 对于演示 STM 的内部工作很有用,但是如果您希望事务在实际代码中产生副作用,您应该让它生成一个 Clojure 代理来达到此目的(仅当事务成功提交时才会执行其任务)。
dosync
andsynchronized
give access to completely different concurrency abstractions.synchronized
is a way of acquiring and releasing locks. When a thread enters asynchronized
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 thesynchronized
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 theref
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; theprint
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).此外,除了 Michał 的出色回答之外,对于 STM 交易,读取始终会在交易开始时为您提供冻结值,而无需等待任何正在进行的交易完成。
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.
为了让那些寻求帮助的人有一个全面的了解,Clojure 确实有一个
synchronized
模拟。当必须使用 Java 非线程安全类型进行互操作时,它非常有用。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.基本区别在于
Clojure STM 支持乐观并发,而 JAVA 同步是悲观并发
Clojure STM 在存在多个线程之前不会获取锁。如果可变状态被另一个线程更新,则重复 dosync 内的操作。此外,dosync 对于可变状态是必需的。与 JAVA 不同,Clojure 在缺少 dosync 时会抛出非法状态异常。
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.