Lately, I've been thinking about the JMM;
As described in this cookbook jsr133-cookbook, normal-store then volatile-store can not reorder;
can reOrder? | 2nd operation | ||
---|---|---|---|
1st operation | Normal Load Normal Store | Volatile load MonitorEnter | Volatile store MonitorExit |
Normal Load Normal Store | No | ||
Volatile load MonitorEnter | No | No | No |
Volatile store MonitorExit | No | No |
now, I simulated a code scenario here;
public static void main(String[] args) throws Exception {
System.out.println(System.currentTimeMillis());
for (int i = 0; i < 500000 * 8; i++) {
final ReOrderClient client = new ReOrderClient();
Thread t1 = new Thread(client::writer);
Thread t2 = new Thread(client::reader);
t1.start();
t2.start();
}
System.out.println("the end");
}
private static class ReOrderClient {
private boolean flag = false;
private volatile int value = 0;
private void writer() {
flag = true;
value = 2;
}
private void reader() {
if (!flag && value == 2) {
System.out.println("reOrder happened, client.value=" + value);
}
}
}
my-CPU-info:
windows-10
Intel(R) Core(TM) i5-8265U CPU @ 1.60GHz
cpu: 4
L1-cahce: 256 KB
L2-cahce: 1.0 MB
L3-cahce: 6.0 MB
In actual testing, the code execution result:
reOrder happened, client.value=2
// s1:
private void writer() {
flag = true;
value = 2;
}
// s2:
private void writer() {
value = 2;
flag = true;
}
as I think only thread1 reorder, occur s2 scenario, then thread2 will have a chance to print the reorder result; but normal-write then volatile-write cause store-store-fence, why reorder happened?
in this question re-ordering in x86, I know that normal-write then volatile-write can not cause re-order in x86; so Just because the compiler Causes reorder;
why compiler reorder happened, please help me;
The problem is with the reader.
private static class ReOrderClient {
private boolean flag = false;
private volatile int value = 0;
private void writer() {
flag = true;
value = 2;
}
private void reader() {
if (!flag && value == 2) {
System.out.println("reOrder happened, client.value=" + value);
}
}
}
If we look at the reader, we can simplify it to:
r1=flag (plain load)
r2=value (volatile load)
There is nothing that prevents an earlier plain load to be reordered with a later volatile load.
On a JMM level; because of the wrong ordering of the 2 loads, there is a data race since a happens before edge is missing between the store and the load of flag.
You need to flip them around and then you will get a [LoadLoad] between the 2 loads.
private void reader() {
if (value == 2 && !flag) {
System.out.println("reOrder happened, client.flag=" + flag);
}
}
This gives:
r1=value (volatile load)
[LoadLoad]
r2=flag (plain load)
And now the reordering should not happen. On a JMM level we have introduced a happens before edge between the store and the load of 'flag'