codecompleting
codecompleting

Reputation: 9611

Not sure this usage of volatile makes sense, seems to have the same issue

Reading here:

JLS 8.3.1.4 volatile Fields

Without volatile it says

"then method two could occasionally print a value for j that is greater than the value of i, 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 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."

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

Answers (6)

erickson
erickson

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

toto2
toto2

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

Caesar Ralf
Caesar Ralf

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

dimitrisli
dimitrisli

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:

  • i++ is not a single operation but three: a) read variable i, b) increment, c) store. This "triple" operation is not atomic, meaning there is not a guarantee that would be completed without some other thread looking into its inconsistent state. If you want to do that look at AtomicInteger#getAndIncrement()
  • your method one() is not synchronised therefore you can have one thread having completed i++ then the second thread prints and the first thread completes the j++ operation then.

Upvotes: 1

Peter Lawrey
Peter Lawrey

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

Sign
Sign

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

Related Questions