multithreadingjvmshared-memoryvolatilehappens-before

The Java volatile Read Visibility Guarantee


I came across the following excerpt while reading on visibility guarantees provided by the JVM when reading volatile variables : "When thread A writes to a volatile variable and subsequently thread B reads that same variable, the values of ALL variables that were visible to A prior to writing to the volatile variable become visible to B AFTER reading the volatile variable."

I have a question around this guarantee of JVM. Consider the below set of classes :

public class Test {
    public static void main(String[] args) throws InterruptedException {
        POJO p = new POJO();
        new Th1(p).start();
        new Th2(p).start();
    }
}   
    public class Th1 extends Thread {
        private POJO p1 = null;
        public Th1(POJO obj) {
            p1 = obj;
        }
        @Override
        public void run() {
            p1.a = 10; // t = 1
            p1.b = 10; // t = 2
            p1.c = 10; // t = 5;
            System.out.println("p1.b val: " + p1.b); // t = 8
            System.out.println("Thread Th1 finished"); // t = 9
        }
    }
    
    public class Th2 extends Thread {
        private POJO p2 = null;
        public Th2(POJO obj) {
            p2 = obj;
        }
        @Override
        public void run() {
            p2.a = 30; // t = 3
            p2.b = 30; // t = 4
            int x = p2.c; // t = 6
            System.out.println("p2.b value: " + p2.b); // t = 7
        }
    }
    
    public class POJO {
        int a = 1;
        int b = 1;
        volatile int c = 1;
    }

Imagine the 2 threads Th1 and Th2 run in separate CPUs and the order in which their instructions execute is indicated by the comment in each line (in their run methods). The question I have is that : When code "int x = p2.c;" executes at t = 6, variables visible to thread Th2 should be refreshed from main memory as per the above para. The main memory then as I understand would have all the writes from Th1 at this point. What value will the variable p2.b show then when it is printed at t = 7?


Solution

  • There is no happens before edge between the write of a and the read of a. Since they are conflicting actions (at least one of them is a write) and are on the same address, there is a data-race and as a consequence, program behavior is undefined.

    I think the following example explains the behavior of what you are looking for better:

        public class Test {
            public static void main(String[] args) throws InterruptedException {
                POJO p = new POJO();
                new Th1(p).start();
                new Th2(p).start();
            }
        }   
        
        public class Th1 extends Thread {
            private POJO p1 = null;
            public Th1(POJO obj) {
                p1 = obj;
            }
            @Override
            public void run() {
                a=1;
                b=1;
            }
        }
        
        public class Th2 extends Thread {
            private POJO p2 = null;
            public Th2(POJO obj) {
                p2 = obj;
            }
            @Override
            public void run() {
                if(p.b==1)println("a must be 1, a="+p2.a);
            }
        }
        
        public class POJO {
            int a = 0;
            volatile int b = 0;
        }
    

    There is a happens before edge between the write of a and the write of b (program order rule) There is a happens before edge between the write of b and a subsequent read of b (volatile variable rule) There is a happens before edge between the read of b and the read of a (program order rule)

    Since the happens before relation is transitive, there is a happens before edge between the write of a and the read of a. So the second thread should see the a=1 from the first thread.