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.