I got general understanding what volatile
means in Java. But reading
Java SE Specification 8.3.1.4 I have a problem understanding the text beneath that certain volatile example.
class Test {
static volatile int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
This allows method one and method two to be executed concurrently, but guarantees that accesses to the shared values for i and j occur exactly as many times, and in exactly the same order, as they appear to occur during execution of the program text by each thread. Therefore, the shared value for j is never greater than that for i, because each update to i must be reflected in the shared value for i before the update to j occurs. It is possible, however, that any given invocation of method two might observe a value for j that is much greater than the value observed for i, because method one might be executed many times between the moment when method two fetches the value of i and the moment when method two fetches the value of j.
How is
j never greater than i
, but at the same time
any given invocation of method two might observe a value for j that is much greater than the value observed for i
??
Looks like contradiction.
I got j
greater than i
after running sample program. Why use volatile
then? It gives almost the same result without volatile
(also i
can be greater than j
, one of previous examples in specs). Why is this example here as an alternative to synchronized
?
At any one time, then j
is not greater than i
.
This is different from what method two observes because it is accessing the variables i
and j
at different times. i
is accessed first, and then j
is accessed slightly later.
This isn't a direct alternative to the synchronized version because the behavior is different. One difference from not using volatile is that without volatile, values of 0 could always be printed. The increment doesn't ever need to be visible.
The example demonstrates the ordering of volatile accesses. An example that requires this could be something like:
volatile boolean flag = false;
volatile int value;
// Thread 1
if(!flag) {
value = ...;
flag = true;
}
// Thread 2
if(flag) {
System.out.println(value);
flag = false;
}
and thread 2 reads the value that thread 1 set rather than an old value.
I'd like to propose that it's a mistake and the examples were supposed to print j
before i
:
static void two() {
System.out.println("j=" + j + " i=" + i);
}
The novelty in the first example is that, due to update reordering, j
can be greater than i
even when observed first.
The final example now makes perfect sense with some minor edits to the explanation (edits and commentary in brackets):
This allows method
one
and methodtwo
to be executed concurrently, but guarantees that accesses to the shared values fori
andj
occur exactly as many times, and in exactly the same order, as they appear to occur during execution of the program text by each thread. Therefore, the shared value forj
is never [observed to be] greater than that fori
, because each update toi
must be reflected in the shared value fori
before the update toj
occurs. It is possible, however, that any given invocation of methodtwo
might observe a value for [i
] that is much greater than the value observed for [j
], because methodone
might be executed many times between the moment when methodtwo
fetches the value of [j
] and the moment when methodtwo
fetches the value of [i
].
The key point here is that the second update will never be observed before the first update, when using volatile
. The last sentence about the gap between the two reads is entirely parenthetical, and i
and j
were swapped to conform to the erroneous example.
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