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?
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.