In addition to everything said in this other answer (just substitute your hasToWait for finished in that answer), the reason why the code starts working when you add a println is as follows:
println is a synchronized method;
- you call it in both threads;
- this creates a happens-before relationship between the two threads;
- therefore the write to the boolean flag becomes visible to the child thread.
You could say that it starts working mostly by accident: you are piggybacking on the synchronization going on in println.