Putting a breakpoint in a non reachable thread forces it to run

  • A+

I have a strange issue with this code:

class Test {     private static boolean test = false;      public static void main(String[] args) {         new Thread(() -> {             while (true) {                 if (test) {                     System.out.println("Print when breakpoint here!");                     test = false;                 }             }         }, "Thread1").start();          new Thread(() -> {             while (true) {                 System.out.println("Print always");                 try {                     Thread.sleep(2000);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 test = true;             }         }, " Thread2").start();     } } 

As I expect because boolean test is not volatile Thread1 uses local cache value of test and when Thread2 changes it to true Thread1 won't do anything. But when I put a breakpoint at the line System.out.println("Prints when put a breakpoint here!"); It will reaches there and prints the line! What's really happening by putting a breakpoint? Does it force the program to directly read the value of variable from memory? Or something else is happening?


Warning: this answer is based mostly on how .Net debuggers work but I expect similar behavior between two runtimes. I expect JVM to allow per-method re-JIT-ing at run time as it already can replace method with HotSpot JIT.

There is some existing articles and posts about what optimizations are turned off for debugging like AMD: perf when debugging enabled, Side Effects of running the JVM in debug mode, Will Java app slow down by presence of -Xdebug or only when stepping through code?. They hint that at least when there is an exception code takes significantly different code path under debugger which may be how breakpoints are implemented.

Many debuggers turn off optimizations (compile time if you allow to recompile code and JIT time if you debugging existing code) when you debug the code. In .Net world impact is global - when debugger is attached it can switch all future JIT compilations to non-optimized path, I expect Java/JVM to support more granular control to allow de-optimizing only methods that may need to stop in debugger. This is done to allow you to clearly see all values of all variables. Otherwise half of the information sometimes including method calls and local/member variables is not available.

"uses local cache value of test" is optimization (likely at JIT time) - so when you start debugging the code (or enable some sort of step-through with breakpoints) it will switch off optimization and read from memory every time thus essentially making variable close to volatile (still not necessary to behave that way all the time but close).

Depending on debugger you use you may disable such behavior (but debugging will be much harder).


:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: