I wrote down this code:
public class Main {
private boolean stopThread = false;
private int counter = 0;
public static void main(String[] args) throws InterruptedException {
final Main main = new Main();
new Thread(() -> {
try {
System.out.println("Start");
Thread.sleep(100);
System.out.println("Done");
main.stopThread = true;
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
while (!main.stopThread) {
main.counter++;
}
System.out.println(main.counter);
}).start();
System.out.println("End");
}
}
and when I run it, the while loop will be running on forever. I was struggling with this a bit, and I got confused what kind of optimalization JIT applied to this code.
First of I was thinking that this is a problem with visibility of stopThread variable, but even if it is a true the while loop should stop a little bit later than I assigned stopThread to true (when CPU cache from 1st thread got flushed to the main memory), so it cannot be the case. It looks like the JIT hardcoded false to stopThread variable, and if it is true, why this variable is not refreshed periodically somehow on runtime?
Obviously, the volatile keyword fixed that, but it is not answering me to that question because volatile can ensures visibility as well as prevent JIT from number of optimalizations.
Even more, when I change the sleep time to 1ms, the second thread will terminate properly, so I'm pretty sure that this is not about variable visibility.
UPDATE: It is worth to mention that I'm getting non-zero value from the counter when the sleep time is set to 1-10 ms.
UPDATE 2: Additionaly, I can say that -XX:+PrintCompilation shows that in case when the sleep time is set to 100 ms the while loop gets compiled, and the On Stack Replacement happend.
UPDATE 3: Probably this is what I was looking for: https://www.youtube.com/watch?v=ADxUsCkWdbE&feature=youtu.be&t=889. And as I thought - this is one of "optimalization" performed by JIT, and the way to prevent it is to specify variable as volatile, or put loadloadFence as the first line in while loop.
ANSWER: As @apangin said:
This optimization is Loop invariant hoisting. JIT is allowed to move the load of stopThread out of the loop, since it may assume that non-volatile field does not change externally, and JIT also sees that stopThread does not change inside the loop.