There is what java spec says about it:
https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5
"A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field."
But what does "subsequent" mean in terms of concurrency? How can we be sure what is subsequent ans what is previous? Let's write a simple test:
public class VolatileTest {
static /*volatile*/ int counter = 0;
static int zeros = 0;
static int ones = 0;
public static void main(String[] args) throws InterruptedException {
int i = 0;
while (i++ < 1_000_000) {
if (i % 100_000 == 0) System.out.println(i);
Thread thread1 = new Thread(VolatileTest::op1);
Thread thread2 = new Thread(VolatileTest::op2);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
counter = 0;
}
System.out.println("Zeros " + zeros);
System.out.println("Ones " + ones);
}
public static void op1() {
counter = 1;
}
public static void op2() {
if (counter == 0) ++zeros;
if (counter == 1) ++ones;
}
}
The final output will be like
Zeros 2095
Ones 997905
And it is predictable.
But when i uncomment the volatile word in counter variable, i still get some zeros in the answer, which means that read from volatile count variable was before the write, despite java spec claims that
write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field.
Where am i wrong?
A write operation to
volatilevariable is immediately visible to all the threads. More importantly, any write operation that happens before the volatile write will also become visible. So in the following code:For all other threads that run:
The value of
xis not guaranteed to be 1 ifcounteris not volatile. The compiler or the CPU may reorder the memory write operations without thevolatile.