Reputation: 9611
Reading here:
Without volatile
it says
"then method two could occasionally print a value for
j
that is greater than the value ofi
, because the example includes no synchronization and"
class Test {
static volatile int i = 0, j = 0;
static void one() { i++; j++; }
static void two() {
System.out.println("i=" + i + " j=" + j);
}
}
With volatile is says
"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 fori
, because method one might be executed many times between the moment when method two fetches the value ofi
and the moment when method two fetches the value ofj
."
In behaves 'properly' with synchornization, but I'm confused as to what benefit volatile
brings here?
I thought volatile
gaurantees the order is preserved, so I would have thought it SOME cases the value of i might be greater than j
, but not the other way around since that implies the order of incrementing was changed.
Is that a typo in the doc? If not, please explain how j
could be greater than i
when using volatile
.
Upvotes: 4
Views: 157
Reputation: 269637
In "source code order", increments to i
happen-before increments to j
. So, a thread invoking method one()
will always observe that i >= j
. But, when other threads observe those variables, they can see things differently.
There are certain events that establish what the JLS calls "synchronization order." It makes sense to talk about these events (and only these events) as "happening before" others. Writing to a volatile variable is one of these. Without using volatile
, it doesn't make any sense to say that i
is incremented before j
; those writes could be re-ordered, and that re-ordering can be observed by other threads.
A better example for what can happen without volatile
would be this:
static void oneAndAHalf() { System.out.println("j=" + j + " i=" + i);
Even though j
appears to be incremented after i
, and j
is fetched before i
, you could still observe j > i
because the removal of volatile
would permit the operations in one()
to be reordered. Add volatile
, and oneAndAHalf()
will always show i >= j
, as you expect.
If you take away volatile
, then method two()
could print a value for j
that is greater than i
for either of two reasons: because operations have been reordered, or because i
and j
are not treated atomically. The current two()
method doesn't unambiguously illustrate the utility of volatile
. Add volatile
, and you'll get the same output, but the only reason is that the operations are not atomic.
To see a consistent view, where i == j
, both methods could be synchronized
. This would make the increment to both variables appear to be atomic.
Upvotes: 0
Reputation: 5326
You got the behavior of volatile right; you just didn't read what you quote carefully:
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
The order is preserved in one()
, it's that in two()
, i
is fetched and printed, but in the time it takes to print i
both i
and j
might be incremented many times by calls to one()
from other threads and so the printed value for j
will be higher than the printed value of i
.
Upvotes: 1
Reputation: 2233
From what I understand, volatile guarantees that different threads will reference the same variable instead of copying it, i.e., if you update a volatile variable in a thread, all others will have this variable update because they all reference the same. A good example of this can be found at Why Volatile Matters.
The thing about method two is that it isn't Atomic. It won't run in only one CPU cycle. You can divide it in different operations like @Sign stated. Even i++ isn't atomic, as it needs to read variable i, incremente it and store it again at i reference in memory.
Upvotes: 1
Reputation: 21381
The volatile
variable tells the JIT compiler not to perform any optimizations that could affect the ordering of access to that variable. The writes to the volatile variable are always performed in the memory and never on the cache or cpu registers.
Also two more points:
Upvotes: 1
Reputation: 533442
volatile makes reading OR writing thread safe/memory consistent. It doesn't make reading AND writing atomic however. Using volatile
is only fine if only one thread will ever update it.
I suggest you use AtomicInteger instead.
Upvotes: 0
Reputation: 1959
It is saying that in the middle of method two method one could run several and that the value read for j would be higher than the value read for i.
read i run method 1 run method 1 read j
Upvotes: 2