Consider the following simple Java application:
public class Main {
public int a;
public volatile int b;
public void thread1(){
int b;
a = 1;
b = this.b;
}
public void thread2(){
int a;
b = 1;
a = this.a;
}
public static void main(String[] args) throws Exception {
Main m = new Main();
while(true){
m.a = 0;
m.b = 0;
Thread t1 = new Thread(() -> m.thread1());
Thread t2 = new Thread(() -> m.thread2());
t1.start();
t2.start();
t1.join();
t2.join();
}
}
}
QUESTION: Is it possible that reading into local variables will result in thread1::b = 0 and thread2::a = 0?
I could not prove that it could not happen from the JMM standpoint so I went down to analyzing compiled code for x86-64.
Here is what compiler ends up with for methods thread1 and thread2 (code unrelated to the while loop and some comments generated by -XX:+PrintAssembly omitted for simplicity):
thread1:
0x00007fb030dca235: movl $0x1,0xc(%rsi) ;*putfield a
0x00007fb030dca23c: mov 0x10(%rsi),%esi ;*getfield b
thread2:
0x00007fb030dcc1b4: mov $0x1,%edi
0x00007fb030dcc1b9: mov %edi,0x10(%rsi)
0x00007fb030dcc1bc: lock addl $0x0,0xffffffffffffffc0(%rsp) ;*putfield b
0x00007fb030dcc1c2: mov 0xc(%rsi),%esi ;*getfield a
So what we have here is that volatile read is done for free, volatile write requires mfence (or lock add) after.
So thread1's Store can still be forwarded after the Load and therefore thread1::b = 0 and thread2::a = 0 is possible.