Search code examples
javamemoryvolatilejlsjava-memory-model

Memory visibility semantics of two related volatile variables


Consider the following program from the JLS section on volatile fields

class Test {
    static volatile int i = 0, j = 0;
    static void one() { i++; j++; }
    static void two() {
        System.out.println("i=" + i + " j=" + j);
    }
}

Consider two thread are concurrently executing method one() and two().

JLS states the following:

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.

I am really confused by the above citation because it says two contradictory statements:

  1. The shared value for j is never greater than that for i. (emphasis mine).

  2. Value for j that is much greater than the value observed for i.

The first statement makes sense to me because if we combine the program order rule(i++ happening before j++) and the memory visibility guarantee of volatile (happens-before) I can sort of see why the value of j can't exceed i. But I an unable to see how the value of j can can far exceed i.

Can you please help me understand this part.


Solution

  • The actual value of j can never be greater than the actual value of i at any given moment. But when the statement

    System.out.println("i=" + i + " j=" + j);
    

    is executed, i and j are not evaluated at the same moment. j is evaluated after i is. When i is evaluated, j is smaller or equal to i. But by the time j is evaluated, the other thread might have called the method one() many times, and both i and j might thus have been incremented many times. So j is still <= i, but what is printed could be

    i=4 j=257
    

    because it is in fact

    at time t0, i=4
    at time t1, j=257