In Effective Java: item 66, Joshua Bloch gave an example about life failure:
// Broken! - How long would you expect this program to run
class StopTh
tl;dr: It's most likely an "accidental" side effect of println being synchronized.
First, realize that it's not the case that the thread is guaranteed not to finish; it's that it's not guaranteed that it will finish. In other words, the race condition on stopRequested — caused by the fact that one thread is writing to it, and another thread is reading from it, and there's no synchronization between the two threads — means that the JVM may, but is not required to, let the reader see what the writer has done.
So, why does System.out.println change this? Because it's a synchronized method. This doesn't actually give you any guarantees about stopRequested as far as the JLS is concerned, but it does mean that the JVM has to do some things, like acquiring a(n unrelated) lock, and establishing (unrelated) happens-before edges, that make it more likely that the write to stopRequested will be seen across threads.