private static boolean stop = false;
private static volatile boolean stop_volatile = false;
public static void main(String[] args) {
Thread thread0 = new Thread(() -> {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop = true;
stop_volatile = true;
});
Thread thread1 = new Thread(() -> {
while (true) {
if (stop_volatile){
System.out.println("thread1:" + stop);
break;
}
}
});
Thread thread2 = new Thread(() -> {
while (true) {
if (stop){
System.out.println("thread2:" + stop);
break;
}
}
});
thread0.start();
thread1.start();
thread2.start();
}
There are two static fields, stop and stop_volatile, and stop_volatile is marked volatile. thread0 changes both to the true.
thread1 can stop and thread2 runs forever.
Why does thread1 print "thread1:true", why can it get right value from field stop in thread1 always, it is not a volatile field?
In Thread 0, after modifying the value of "stop_volatile," it writes its working cache to memory and notifies other threads that their working cache of the changed value is invalidated. Then, when other threads access this variable, they need to reload it from memory. Here, "stop" is a non-volatile variable. Why does Thread 1 not continue to use the cached value and instead reloads it as well?
The read of a volatile field will make the thread see all changes that occurred before the last write to that field (the so called happens-before relationship). So, given thread1 repeatedly reads the volatile
stop_volatile, and the write tostopoccurred before the write tostop_volatile, thread1 is guaranteed to see the changes tostopas well.And as Joachim Sauer points out in the comments, this is the one and only time thread1 accesses
stop, so there was nothing "cached" previously for that thread, while thread2 continuously accessesstop, so it is more likely to see a "cached" value (or it might even be optimized toif (false)!).Though it is tough to read, you may want to read the 17.4 Memory Model section of the Java Language Specification, which details this.
What basically happens is that you have the following actions:
stop = truehappens-beforestop_volatile = truestop_volatile = trueis a synchronization actionstop_volatileis readstop_volatileis also a synchronization actionstop_volatilehappens-before the read tostop_volatileAs specified in section 17.4.5 Happens-before Order:
In other words (using t0 and t1 as shorthand for thread0 and thread1), given:
stop(t0), write tostop_volatile(t0)), andstop_volatile(t0), read fromstop_volatile(t1)), also meansstop(t0), read fromstop_volatile(t1)),And so the subsequent read on thread1 will see the updated value of
stop.You can see this by working out the happens-before further: hb(read from
stop_volatile(t1), read fromstop(t1)) implies hb(write tostop(t0), read fromstop(t1)).