2
votes

I read following about volatile from the book Java Concurrency in Practice:

When a field is declared volatile, the compiler and runtime are put on notice that this variable is shared and that operations on it should not be reordered with other memory operations. Volatile variables are not cached in registers or in caches where they are hidden from other processors, so a read of a volatile variable always returns the most recent write by any thread.

The visibility effects of volatile variables extend beyond the value of the volatile variable itself. When thread A writes to a volatile variable and subsequently thread B reads that same variable, the values of all variables that were visible to A prior to writing to the volatile variable become visible to B after reading the volatile variable. So from a memory visibility perspective, writing a volatile variable is like exiting a synchronized block and reading a volatile variable is like entering a synchronized block.

I am confused with the last sentence above. Say variable x is defined volatile and before modifying x, u,v and w were visible to thread A, then when thread B reads x afterwards, it will also be able to read latest values of u,v and w. Can we specify for same for synchronized?.

Q1. That is, is below correct?

Variables u,v and w were visible to thread A while exiting synchronized block, then the latest values of u,v and w will be visible to thread B entering synchronized block afterwards.

I feel above fact is incorrect as u,v and w may be stored in caches and registers as they are not defined volatile. Am I correct with this? So visibility is not ensured by synchronized (and also by locks and atomic variables as they are similar to synchronized)

The book further says:

Locking can guarantee both visibility and atomicity; volatile variables can only guarantee visibility.

But I feel following:

  1. Locks, synchronized and atomic variables only guarantee read-write atomicity (not visibility and protection from reordering).
  2. volatile guarantee visibility and protection from reordering by compiler and runtime (not read-write atomicity).

Q2. Am I correct with above two points?

2
Yeah I will need to read them. But, can you please answer my questions with minimal words? Say, with just Yes and No? - anir
"volatile variables can only guarantee visibility." Incorrect: an access to a volatile variable is always atomic. But multiple accesses cannot be made atomic. Only locking a mutex can make a sequence of operations appear atomic, but only from the POV of other threads that use the same mutex. - curiousguy
@anir, If you read my comment above, which was intended for curiousguy, don't forget that a statement like i++ is not a single "access." It means; fetch the value of i, and then store the incremented value back in to i. I.e., it's a sequence of two "accesses." - Solomon Slow
@curiousguy Looks like I made a mistake there: The JLS promises that "Writes and reads of volatile long and double values are always atomic." But what I said earlier (comment now deleted) still is mostly true: Writes and reads of all other types of variable are atomic whether you declare them to be volatile or not. It's only in the special case of long or double that volatile adds anything in the atomicity department. - Solomon Slow

2 Answers

1
votes

1) Locks, synchronized and atomic variables guarantee read-write atomicity and visibility and protection from reordering

2) volatile guarantees visibility and protection from reordering by compiler and runtime

read-write atomicity of volatile fields is a little bit tricky: reading and writing to a volatile field is atomic, for example if you write to a volatile long (64 bit) on a 32 bit jvm the read and the write is still atomic. You always read the complete 64 bit. But operations like ++ on a volatile int or long are not atomic

1
votes

Hopefully the following is accurate... this is my current as-simple-as-possible-but-no-simpler understanding....

Q1. That is, is below correct?

Variables u,v and w were visible to thread A while exiting synchronized block, then the latest values of u,v and w will be visible to thread B entering synchronized block afterwards.

I'm assuming here by "latest values" you actually mean "latest values at the time the synchronized block was exited"...

Then yes - with the caveat of course that both A and B must synchronize on the same object.

Side note: of course, a similar caveat applies to the visibility guarantee of volatile - A and B must write and read (respectively) the same volatile field.

But I feel following:

  1. Locks, synchronized and atomic variables only guarantee read-write atomicity (not visibility and protection from reordering).
  2. volatile guarantee visibility and protection from reordering by compiler and runtime (not read-write atomicity).

Q2. Am I correct with above two points?

Correct for #2 but incorrect for #1...

synchronized guarantees visibility as well as atomicity. The idea of "visibility" is also described as there being a happens-before relationship.

In other words, thread A exiting synchronized (x) happens-before thread B entering synchronized (x).

Similarly, a write to volatile field x happens-before a read of volatile field x.

In other words, with regards to visibility, synchronized enter/exit pairs give you exactly the same guarantee as volatile read/write pairs.

But synchronized pairs guarantee both visibility and atomicity, while volatile pairs guarantee only visibility.

Oops - forgot one exception: volatile long and volatile double does guarantee that reads and writes of these 64-bit values will be atomic (i.e., will avoid "word tearing").

Another way to look at it: having a volatile field x is kind of like having a tiny synchronized (x') around each read or write of x, where x' is some otherwise invisible lock object corresponding to x (it's not quite the same, because with volatile you have to pair reads with writes, whereas all synchronized keywords work the same way).

I feel above fact is incorrect as u,v and w may be stored in caches and registers as they are not defined volatile. Am I correct with this?

This is somewhat surprising, but the guarantees of visibility provided by synchronized and volatile apply to everything that is visible to the two threads, and are not limited only to the object being locked, the volatile field itself, other fields in the same object, etc.

That's why it kindof makes sense to think of them in terms of memory barriers, if you are familiar with low level assembly/kernel programming, etc.