how to understand volatile example in Java Language Specification?

The examples are more than “a little wrong”.

First, you are right that even without reordering, j may appear greater than i in this example. This is even acknowledged later in the same example:

Another approach would be to declare i and j to be volatile:

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.

Of course, it is abstruse to say “the shared value for j is never greater than that for i”, just to say right in the next sentence “It is possible … [to] observe a value for j that is much greater than the value observed for i”.

So j is never greater than i, except when it is observed to be much greater than i? Is it supposed to say that “a little greater” is impossible?

Of course not. This statement makes no sense and seems to be the result of trying to separate some objective truth like “the shared value” from “the observed value” whereas in fact, there is only observable behavior in a program.

This is illustrated by the wrong sentence:

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.

Even with volatile variables, there is no such guarantee. All that the JVM must guarantee, is that the observed behavior doesn’t contradict the specification, so when you invoke one() thousand times in a loop, for example, an optimizer may still replace it with an atomic increment by thousand, if it can preclude the possibility of another thread witnessing the presence of such an optimization (other than deducing from the higher speed).

Or in other words, how many times a variable (resp. its memory location) is actually accessed, is not observable and hence, not specified. It doesn’t matter anyway. All that matters to an application programmer, is that j can be greater than i, whether the variables are declared volatile or not.

Swapping the order of the reads of i and j within two() might make it a better example, but I think, it would be best, if JLS §8.3.1.2 did not try to explain the meaning of volatile colloquially, but just stated that it imposes special semantics according to the memory model and left it to the JMM to explain it in a formally correct way.

Programmers are not supposed to master concurrency just by reading 8.3.1.4., so the example is pointless here (in the best case; the worst case would be creating the impression that this example was sufficient to understand the matter).


What Holger is saying in his answer is absolutely correct (read it again and accept it), I just want to add that using jcstress, this is even sort of easy to prove. The test itself is just a minor refactor from the Coherence Sample (which is superbe! IMO):

import org.openjdk.jcstress.annotations.Actor;
import org.openjdk.jcstress.annotations.Expect;
import org.openjdk.jcstress.annotations.JCStressTest;
import org.openjdk.jcstress.annotations.Outcome;
import org.openjdk.jcstress.annotations.State;
import org.openjdk.jcstress.infra.results.II_Result;

@JCStressTest
@Outcome(id = "0, 1", expect = Expect.ACCEPTABLE_INTERESTING, desc = "only j updated")
@Outcome(id = "1, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "only i updated")
@Outcome(id = "0, 0", expect = Expect.ACCEPTABLE_INTERESTING, desc = "both updates lost")
@Outcome(id = "1, 1", expect = Expect.ACCEPTABLE, desc = "both updated")
@State
public class SOExample {

    private final Holder h1 = new Holder();
    private final Holder h2 = h1;

    @Actor
    public void writeActor() {
        ++h1.i;
        ++h1.j;

    }

    @Actor
    public void readActor(II_Result result) {
        Holder h1 = this.h1;
        Holder h2 = this.h2;

        h1.trap = 0;
        h2.trap = 0;

        result.r1 = h1.i;
        result.r2 = h2.j;
    }

    static class Holder {

        int i = 0;
        int j = 0;

        int trap;
    }

}

Even if you don't understand the code, the point is that running it will show ACCEPTABLE_INTERESTING as absolutely possible outcomes; be that with volatile int i = 0; volatile int j = 0; or without that volatile.