ReentrantLock API doc claims that it is identical to using synchronization
ReentrantLock API doc claims that it is identical to using synchronization. If that is right, I am not sure how they enforce memory barrier operations(like it does in synchronization) for all the variables accessed between the acquire/release lock operations . can some one throw some light on this?
They do it by using memory-barrier instructions as needed, just as is done for use of synchronized. This is done by the VM.
Is it part of the Lock Implementation?
> Is it part of the Lock Implementation?Yes. Most of the synchronizers in j.u.c are built on the AbstractQueuedSynchronizer and it uses volatiles and atomics to get the necessary memory semantics. volatiles and atomics in turn get them from the VM.
I am curious why the documentation for the java.util.concurrent doesn't mention memory barriers. Not that I doubt your response, but the documentation is very good for this package, and it seems an odd omission.
> I am curious why the documentation for the
> java.util.concurrent doesn't mention memory
> barriers. Not that I doubt your response, but the
> documentation is very good for this package, and it
> seems an odd omission.
There are a number of mentions of "memory synchronization" - see for example the section by that name in the Lock javadocs. The docs don't say how this is implemented (which would be by memory barrier instructions, or fence instruction or whatever synchronization instructions that are on a given platform.
Thank you for the response.I guess I was too focused on the specific JavaDoc for ReentrantLock. You are right - the Lock Interface explicitly states the requirement I was looking for.
From http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/locks/Lock.html:
"All Lock implementations must enforce the same memory synchronization semantics as provided by the built-in monitor lock:
* A successful lock operation acts like a successful monitorEnter action
* A successful unlock operation acts like a successful monitorExit action"
> "All Lock implementations must enforce the same memory
> synchronization semantics as provided by the built-in monitor lock:
>* A successful lock operation acts like a successful monitorEnter action
> * A successful unlock operation acts like a successful monitorExit action"
Does it means that all varaibles in thread memory must be updated like
when we enter a synchronized block? If, so I don't see how it is done. Can you help me?
> Does it means that all varaibles in thread memory
> must be updated like
> when we enter a synchronized block? If, so I don't
> see how it is done. Can you help me?
Threads don't have a "memory". The old Java memory model talked about a conceptual model where threads had a local working memory and different actions would cause values to shift between main memory and a thread's local memory. The conceptual model turned out to be a problem for anumber of reasons - the main one being that people equated it with caches and therefore used the behaviour of caches to explain the memory model - and that doesn't work.
The new Java memory model establishes a partial ordering between actions - known as a happens-before ordering. In terms of locks and use of synchronized everything that happens-before the release of a lock A, happens-before any subsequent acquisition of that same lock. So if thread 1 writes x=5 then releases lock A, and thread 2 acquires lock A and reads the value of x, then it is guaranteed to read 5. (No other threads are involved.)
Actually enforcing this happens-before order is not that hard and it depends on the platform as to what needs to be done. Generally it means the JIT compiled code ensures that any register held values are written to a main memory location at the appropriate time - eg just before releasing the lock - and that the VM issues any necessary "memory barrier" instructions (fences, membars, sync's - depending on platform) to ensure the ordering is met.
For most modern hardware platforms no explicit cache flushing is needed by the software as the hardware takes care of cache coherency.
Thanks for your explanation!
Let's see if I understand correctly:
If I implements the Lock interface, the VM will take care to respect the "happens-before" regarding variables when the lock() and unlock() method are called. I don't have to explicitly enforce it (like forcing a monitorEnter/monitorExit call with Unsafe or using a synchronized block).
Am I right?
Hi,
you might want to check jdk6.0 documentation
http://java.sun.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility
Memory Consistency Properties
Chapter 17 of the Java Language Specification defines the happens-before relation on memory operations such as reads and writes of shared variables. The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation. The synchronized and volatile constructs, as well as the Thread.start() and Thread.join() methods, can form happens-before relationships. In particular:
* Each action in a thread happens-before every action in that thread that comes later in the program's order.
* An unlock (synchronized block or method exit) of a monitor happens-before every subsequent lock (synchronized block or method entry) of that same monitor. And because the happens-before relation is transitive, all actions of a thread prior to unlocking happen-before all actions subsequent to any thread locking that monitor.
* A write to a volatile field happens-before every subsequent read of that same field. Writes and reads of volatile fields have similar memory consistency effects as entering and exiting monitors, but do not entail mutual exclusion locking.
* A call to start on a thread happens-before any action in the started thread.
* All actions in a thread happen-before any other thread successfully returns from a join on that thread.
The methods of all classes in java.util.concurrent and its subpackages extend these guarantees to higher-level synchronization. In particular:
* Actions in a thread prior to placing an object into any concurrent collection happen-before actions subsequent to the access or removal of that element from the collection in another thread.
* Actions in a thread prior to the submission of a Runnable to an Executor happen-before its execution begins. Similarly for Callables submitted to an ExecutorService.
* Actions taken by the asynchronous computation represented by a Future happen-before actions subsequent to the retrieval of the result via Future.get() in another thread.
* Actions prior to "releasing" synchronizer methods such as Lock.unlock, Semaphore.release, and CountDownLatch.countDown happen-before actions subsequent to a successful "acquiring" method such as Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await on the same synchronizer object in another thread.
* For each pair of threads that successfully exchange objects via an Exchanger, actions prior to the exchange() in each thread happen-before those subsequent to the corresponding exchange() in another thread.
* Actions prior to calling CyclicBarrier.await happen-before actions performed by the barrier action, and actions performed by the barrier action happen-before actions subsequent to a successful return from the corresponding await in other threads.
