dosync
并synchronized
允许访问完全不同的并发抽象。
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 代理来达到此目的(只有在事务发生时才会执行其任务成功提交)。