1

I'm trying to convince myself that clojure is indeed easier than java for concurrency programming.

but I feel that the Clojure Refs/do-sync is almost exactly the same as java "synchronized" block. then I read this thread : Clojure STM ( dosync ) x Java synchronize block

--- I am restarting a new thread since if I comment there in the old thread, the response may not be high due to the old age.

the first comment in that thread by Michał Marczyk claims that the diff is that java sync block uses locks while Clojure uses transactions. I think this claim does not touch the essence of the problem: at the bottom, transactions are still implemented by locks. so "java using locks" is not the reason Clojure is better.

I think the real benefit is that Clojure transactions manages the locks automatically, just as DB transactions do. this way, the order to acquire locks, and the order to play transactions are determined by the transaction manager, so that programmer does not need to care about that, while in the java world, the programmer has to explicitly choose which lock to use for the sync block, which leads to possible deadlocks. transaction manager could use 2-phase locking for example to avoid deadlocks.

does the above make sense?

thanks Yang

Community
  • 1
  • 1
teddy teddy
  • 3,025
  • 6
  • 31
  • 48

2 Answers2

6

Ref in Clojure is a different concurrency abstraction and it works like a database transaction - it has Atomicity, Consistency and Isolation properties. It's built on top of JVM locking mechanism so it's possible to implement it yourself in Java. The reason we don't do it is that Ref like mechanism requires other non-trivial mechanisms to be implemented beforehand:

Grzegorz Luczywo
  • 9,962
  • 1
  • 33
  • 22
  • 1
    +1 to this. Also adding my own answer, since it's my older answer that's explicitly called out by the question and there's some extra comments I'd like to make. – Michał Marczyk Apr 17 '13 at 21:21
5

Author of the referenced answer here -- let me try and elaborate:

In actual fact in the referenced answer I claim, first and foremost, that "dosync and synchronized give access to completely different concurrency abstractions". Further, I describe synchronized not merely as "using locks", but rather as "a way of acquiring and releasing locks".

In other words, while the STM is certainly using the locks under the hood, it exposes transactional semantics which can be reasoned about as such; for example, it is free of deadlocks by construction (livelock, however, is possible). In contrast, synchronized is explicitly nothing more or less than a way for the programmer to declare that the monitor of such and such an object is to be acquired and released at the points marked by the braces. The important difference here is about semantics, not implementation.

In addition, there's more to STM than managing locking order; there's MVCC, as mentioned by Grzegorz, there's automatically restarting transactions, there's ensure and commute (controlling the consistency vs. concurrency tradeoff, if you will), there's the cooperation between the STM and Agents (Agent actions put on the queue with send only get dispatched at commit time if called from within a transaction).

So, it is certainly true that STM is a mechanism for managing locks, and it is useful to describe it as such; but the bigger picture is that the STM implementation uses whichever internal details it needs, locks included, to provide the programmer with an alternative concurrency abstraction, reasonably non-leaky, which ultimately must be approached as such if one is to extract the full benefit from it.

Michał Marczyk
  • 83,634
  • 13
  • 201
  • 212