简体   繁体   中英

Java Memory Model interaction of synchronization, volatile and (stamped) locks

Is the volatile modifier required when working with locks to guarantee memory visibility?

Trying to fully understand concurrency, memory visibility and execution control I came across several sources saying that variables updated in synchronized blocks do not require the field to be volatile (mostly no sources given and actually one page saying synchronized methods and volatility fields need to be used in conjunction).

When approaching the jls chapter 17.4.5 I found:

Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is visible to and ordered before the second.

Is this the section which says that subsequent synchronized method calls guarding the same variable variable will ensure it to be visible to the second thread? If this is the case does the same hold true for locks since we can also guarantee an order?

On the other hand what happens when suddenly we have write locks allowing 2 threads to access the field. Does the entire construct collapse and threads are never guaranteed to updated their cache even in the event if the variable is unlocked?

In short code

int field; //volatile not needed because we have a definite happens-before relationship
Lock lock;

void update(){
    //No matter how many threads access this method they will always have 
    //the most up to date field value to work with.
    lock.lock()
    field *= 2;
    lock.unlock();
}

From the API documentation for Lock :

https://docs.oracle.com/javase/10/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, as described in Chapter 17 of The Java™ Language Specification:

  • A successful lock operation has the same memory synchronization effects as a successful Lock action.
  • A successful unlock operation has the same memory synchronization effects as a successful Unlock action.

Unsuccessful locking and unlocking operations, and reentrant locking/unlocking operations, do not require any memory synchronization effects.

That's a little unclear imo but the gist of it is that yes, Lock is required to work the same way as a monitor (what the synchronized keyword does) and therefore your example does always make the most recent update of field visible without explicitly using the volatile keyword.

PS Get Brian Goetz's Java Concurrency in Practice, it explains all of this stuff in a lot more detail. It's basically the bible of all things concurrency in Java.

...and actually one page saying synchronized methods and volatility fields need to be used in conjunction.

You can distill everything you need to know about memory visibility and synchronized blocks down to one simple rule. That is, whatever thread A does to shared variables and objects before it exits from a synchronized (o) {...} block is guaranteed to become visible to thread B by the time thread B enters a synchronized (o) {...} block for the same object, o .

And, as @markspace already said, any implementation of java.util.concurrent.locks.Lock is required to work in the same way.

Is the volatile modifier required when working with locks to guarantee memory visibility?

volatile variable only guarantees memory visibility but not the atomicity. This is one of the main difference between volatile and the synchronized block in Java. So when you use synchronized blocks, variables do not have to be volatile . But If your variable is volatile and performing any compound actions on that variable, then you need to guard the update to the volatile variable with the lock.

Is this the section which says that subsequent synchronized method calls guarding the same variable will ensure it to be visible to the second thread? If this is the case does the same hold true for locks since we can also guarantee an order?

Yes. Because locks will give you both visibility and atomicity.

On the other hand what happens when suddenly we have write locks allowing 2 threads to access the field. Does the entire construct collapse and threads are never guaranteed to updated their cache even in the event if the variable is unlocked?

If you guarding the update to the variable on the same lock only one thread can work on that variable at any given time. So it guarantees consistency. But If you use different locks every time to guard that variable, then more than one thread will modify the variable state and can potentially make the variable state inconsistent. So, in this case, both visibility and atomicity are guaranteed but still, it can lead to inconsistency.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM