15

The following is from classical Concurency in Practice:

When thread A writes to a volatile variable and subsequently thread B reads the 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.

I am not sure I can really understand this statement. For example, what is the meaning of all variables in this context? Does this mean that using volatile also has side-effects to the usage of non-volatile variables?
It seems to me that this statement has some subtle meaning that I can not grasp.
Any help?

Cratylus
  • 52,998
  • 69
  • 209
  • 339

2 Answers2

14

The answer to your question is in JLS #17.4.5:

A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.

So if in one thread you have

aNonVolatileVariable = 2 //w1
aVolatileVariable = 5 //w2

And subsequently in another thread:

someVariable = aVolatileVariable //r1
anotherOne = aNonVolatileVariable //r2

You have the guarantee that anotherOne will be equal to 2, even if that variable is not volatile. So yes, using volatile also has side-effects to the usage of non-volatile variables.

In more details, this is due to 2 other guarantees provided by the Java Memory Model (JMM) in that same section: intra thread order and transitivity (hb(x,y) means x happens before y):

If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
[...]
If hb(x, y) and hb(y, z), then hb(x, z).

In my example:

  • hb(w1, w2) and hb(r1, r2) (intra thread semantics)
  • hb(w2, r1) because of the volatile guarantee

so you can conclude that hb(w1, r2) by transitivity.

And the JMM guarantees that all executions of a program will be sequentially consistent (i.e. will look like nothing has been reordered) if it is correctly synchronized with happens-before relationships. So in this specific case, the non-volatile read is guaranteed to see the effect of the non-volatile write.

assylias
  • 321,522
  • 82
  • 660
  • 783
  • 1
    I am sorry, I can not see how this guarantee follows from the statement of `JSL`.All it says is that a write to a volatile `happens-before` a read of the field.How does this also guarantee that in the other thread `anotherOne` is visible to be `2`? – Cratylus Sep 16 '12 at 10:34
  • 1
    I understand that `hb(w1, r2)`.Ok so far.But why is it mandated here, that the updated value of `2` is visible?Couldn't it be, that the write **happens before** but the read uses a cached value instead and not the value from the `w1`?This is the part I can not get.Does the definition of `hb(w1, r2)` include also the visibility aspect? – Cratylus Sep 16 '12 at 11:58
  • @Cratylus The [JMM guarantees that a program that is correctly synchronized (with happens-before relationships) will be sequentially consistent](http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5-400): *"This is an extremely strong guarantee for programmers. [...] Once the determination that the code is correctly synchronized is made, the programmer does not need to worry that reorderings will affect his or her code."* – assylias Sep 16 '12 at 12:03
  • @Cratylus Another quote from JLS in the same section: *"In a happens-before consistent set of actions, each read sees a write that it is allowed to see by the happens-before ordering."* – assylias Sep 16 '12 at 12:05
  • Don't these remarks address the non-visibility due to re-ordering?Ok reordering will not occur, but what about caching values in registers? – Cratylus Sep 16 '12 at 12:22
  • @Cratylus "Sequentially consistent" guarantees visibility. Here is another quote (17.4.3): *"Sequential consistency is a very strong guarantee that is made about visibility and ordering in an execution of a program. Within a sequentially consistent execution, there is a total order over all individual actions (such as reads and writes) which is consistent with the order of the program, and each individual action is atomic and is immediately visible to every thread."* – assylias Sep 16 '12 at 13:00
9

It means if you write to ten non-volatile variable and write to a volatile one, all the non-volatile variables must be set before the volatile one.

If you read the volatile variable and all the non-volatile ones you can be sure that the order won't be swapped around.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • What order are you talking about?The `volatile` variable can not be re-ordered, ok.So the `volatile` variable should not be reoreder with non-volatile and be writen last.But the order of the non-volatile variables could still be changed, right? – Cratylus Sep 15 '12 at 15:12
  • 2
    The order of the values set in volatile variables cannot change relative to other variable (volatile or otherwise) but this can happen for non-volatile ones. This means when you set a volatile variable all the previous values set will be made cache coherent even though they are not volatile. – Peter Lawrey Sep 15 '12 at 15:15
  • What is cache-coherent?That they will not be cached either? – Cratylus Sep 15 '12 at 15:17
  • They will be cached, but the CPU ensures every cache sees the same value (or will ask for it on demand) Each Socket has multiple caches with 2 or 3 levels. They have a communication bus to ensure they are in sync with each other when they need to be. Even multiple sockets will communicate to keep in sync avoid having to write/read the value from main memory. – Peter Lawrey Sep 15 '12 at 15:20
  • But why is this synchronization needed and guaranteed for the non-volatile variables?How are they affected just because they *happen* to come before the write to a `volatile`? – Cratylus Sep 15 '12 at 15:24
  • synchronization provides more guarantees such as a read and a write in a consistent manner e.g. an increment. only reading or writing is often not what you need. The definition of volatile is such that all writes which occur before it must not occur after it. – Peter Lawrey Sep 15 '12 at 15:28
  • `all writes which occur before it must not occur after it`.I understand this.But how does this depend on/has side-effect on cache-coherence?This is the part I don't follow – Cratylus Sep 15 '12 at 15:32
  • I think there is some confusion here. I've seen many people comment that the volatile keywords affect processor cache coherence of first and second level cache etc. I don't see how this can possibly be true. For one thing the cache coherence protocol guarantees that that multiple thread's view of the same memory location are always in sync. It is more likely that the VM maintains a separate memory cache area for each thread, and the volatile keyword invokes some code that acts to synchronize them. – Darren Oct 24 '12 at 15:20
  • The JVM only uses instructions which either perform cache coherent reads and write or simple reads and writes which don't enforce cache coherency. It doesn't do anything the CPU doesn't support natively. Additionally the JVM can optimises reads of non-volatile fields in ways which can mean they never see an update because they are not altered in the same thread. – Peter Lawrey Oct 24 '12 at 15:25
  • Peter, my point is that I don't think the cache you are referring to is the same as the processor cache. It would instead be a cache maintained by the VM. In other words, if two threads read two different values then they are reading from two different memory addresses. If two threads read from the same address, then the processor's cache coherence protocol would guarantee that they both receive the same value. That is my understanding. If I am wrong then I would like to know. – Darren Oct 24 '12 at 19:01
  • The VM doesn't maintain such a cache. When you read the same address from two different caches you can get different values. Cache coherence is not enforced for all CPU instructions. – Peter Lawrey Oct 24 '12 at 21:42