11

Clojure STM (dosync) 方法和 Java 同步 Block 有什么区别?

我正在从“睡觉的理发师”问题中阅读下面的代码。(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)”?它会阻止这个关键代码吗?

提前致谢 !丹塔斯

4

4 回答 4

21

dosyncsynchronized允许访问完全不同的并发抽象。

synchronized是一种获取和释放锁的方式。当一个线程进入一个synchronized块时,它会尝试获取适当的锁;如果锁当前由不同的线程持有,则当前线程阻塞并等待它被释放。这会导致某些问题,例如死锁的风险。当线程离开synchronized块时,锁被释放。

dosync标记要在事务中运行的代码块。Clojure 中的事务是一种协调对 Refs(使用ref函数创建的对象)的更改的方式;如果您需要一些代码来对 Clojure 中的某些可变状态具有一致的视图——并且可能会更改它们——你将它们放在 Refs 中并在事务中执行你的代码。

一个事务有一个有趣的属性,如果由于某种原因它不能提交,它会重新启动,达到一定的最大重试次数(目前硬编码为 10000)。交易无法提交的可能原因之一是无法获得一致的世界观(实际上,相关的参考文献——有一个“自适应历史”设施,这使得这个问题不像看起来那么严重乍一看); 其他交易同时进行的更改;等等

事务没有死锁的风险(除非程序员不遗余力地通过 Java 互操作引入与 STM 系统无关的死锁);另一方面,活锁是一种可能性,尽管可能性不大。一般来说,很多——尽管不是全部!-- 程序员与数据库事务相关联的直觉在 STM 系统的上下文中是有效的,包括 Clojure 的上下文。

STM是一个巨大的话题;了解 Clojure STM 的一个极好的资源是 Mark Volkmann 的Software Transactional Memory文章。它在最后几节中深入讨论了 Clojure 的 STM,但开头可以作为很好的介绍性读物。

至于您引用的片段,实际上您通常不想在生产代码中模拟它,因为dosync块应该几乎总是没有副作用;print这里对于演示 STM 的内部工作很有用,但是如果您希望事务在实际代码中引起副作用,您应该让它产生一个 Clojure 代理来达到此目的(只有在事务发生时才会执行其任务成功提交)。

于 2010-08-27T11:47:44.313 回答
3

此外,除了 Michał 的出色回答之外,对于 STM 事务,读取始终会在事务开始时为您提供冻结值,而无需等待任何正在进行的事务完成。

于 2010-08-27T13:39:14.767 回答
3

只是为了给那些寻求的人一个完整的画面,Clojure 确实有一个synchronized模拟。当必须使用 Java 非线程安全类型进行互操作时,它很有用。

(locking x & body)
于 2010-11-03T00:29:05.227 回答
0

基本区别如下

Clojure STM 支持乐观并发,而 JAVA 同步是悲观的

Clojure STM 在有多个线程之前不会获取锁。如果可变状态由另一个线程更新,则重复dosync内部的操作。此外,对于可变状态, dosync是强制性的。与 JAVA 不同,当 dosync 丢失时,Clojure 会抛出非法状态异常。

于 2016-04-10T16:21:11.227 回答