There a multiple code examples which assume that the following instructions (1) and (2) cannot be reordered:
int value;
volatile boolean ready;
// ...
value = 1; // (1)
ready = true; // (2)
The latter Stack Overflow answer refers to JLS §17.4.5:
If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
However I don't understand why this should apply here since the JLS Example 17.4-1 also states:
[...] compilers are allowed to reorder the instructions in either thread, when this does not affect the execution of that thread in isolation.
which is clearly the case here.
All other definitions in the JLS specific to volatile
are only with respect to the same volatile variable, but not to other actions:
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
It confuses me where people see the guarantee that usage of volatile (read or write) may not be reordered.
Could you please base you explanation on the JLS or on other sources which are based on the JLS.
In isolation, your code doesn't guarantee anything. There's a second thread involved here and we need its code too! There's a reason why the tutorials you linked to show both threads.
If the code of the two threads is this:
int value;
volatile boolean ready;
// Thread - 1
value = 1; // (1)
ready = true; // (2)
// Thread - 2
if (ready) { // (3)
x = value // (4)
}
Then we have a happens-before relationship between (1) and (2) due to program order:
If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
and we have a happens-before relationship between (2) and (3) due to ready
being volatile:
A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
And we have a happens-before relationship between (3) and (4) due to program order again:
If x and y are actions of the same thread and x comes before y in program order, then hb(x, y).
So there's a happens-before chain (1) → (2), (2) → (3), (3) → (4)
And since happens-before is a transitive relation (if A happens before B and B happens before C, then A happens before C) that means that (1) happens before (4).
If we flip (3) and (4) so that the second thread reads value
before reading ready
, then the happens-before chain breaks, and we no longer get any guarantees about the read from value
.
Here's a nice tutorial with some more JMM pitfalls, including this one.
Isn't the Java memory model fun?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With