5

In the following scenario, the boolean 'done' gets set to true which should end the program. Instead the program just keeps going on even though the while(!done) is no longer a valid scenario thus it should have halted. Now if I were to add in a Thread sleep even with zero sleep time, the program terminates as expected. Why is that?

public class Sample {

    private static boolean done;

    public static void main(String[] args) throws InterruptedException {
        done = false;
        new Thread(() -> {
            System.out.println("Running...");
            int count = 0;
            while (!done) {
                count++;
                try {
                    Thread.sleep(0); // program only ends if I add this line. 
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();

        Thread.sleep(2000);

        done = true; // this is set to true after 2 seconds so program should end.
        System.out.println("Done!"); // this gets printed after 2 seconds
    }

}

EDIT: I am looking to understand why the above needs Thread.sleep(0) to terminate. I do not want to use volatile keyword unless it is an absolute must and I do understand that would work by exposing my value to all threads which is not my intention to expose.

kar
  • 4,791
  • 12
  • 49
  • 74
  • 2
    try `private static volatile boolean done;` – Felix Oct 30 '19 at 23:56
  • 1
    Does this answer your question? [What is the volatile keyword useful for](https://stackoverflow.com/questions/106591/what-is-the-volatile-keyword-useful-for) – Felix Oct 30 '19 at 23:58
  • Kind of answering your edit: It seems like a call to `Thread.sleep` leads to the visibility update of your `done` flag, but that seems to be something JVM-Implementation specific "feature" on which you should really not rely on. Whenever you want to see the most recent value written to a variable by another thread you should use the `volatile` keyword – Felix Oct 31 '19 at 00:07

2 Answers2

4

Each thread have a different cached version of done created for performance, your counter thread is too busy making the calculations for count that it doesnt give a chance to reload done.

volatile ensures that any read/write is done on the main memory, always update the cpu cache copy.

Thread.sleep always pause the current thread, so even if 0 your counter thread is interrupted by some time <1ms, that is enough time for the thread to be adviced of done variable change.

Jassiel Díaz
  • 89
  • 1
  • 5
2

I am no Java expert man, I don't even program in java, but let me try.

A thread on stackoverflow explains the Java Memory model: Are static variables shared between threads?

Important part: https://docs.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility

Chapter 17 of the Java Language Specification defines the happens-before relation on memory operations such as reads and writes of shared variables. The results of a write by one thread are guaranteed to be visible to a read by another thread only if the write operation happens-before the read operation. The synchronized and volatile constructs, as well as the Thread.start() and Thread.join() methods, can form happens-before relationships.

If you go through the thread, it mentions the "Happens before" logic when executing threads that share a variable. So my guess is when you call Thread.sleep(0), the main thread is able to set the done variable properly making sure that it "Happens first". Though, in a multi-threaded environment even that is not guaranteed. But since the code-piece is so small it makes it work in this case.

To sum it up, I just ran your program with a minor change to the variable "done" and the program worked as expected:

private static volatile boolean done;

Thank you. Maybe someone else can give you a better explanation :P

AmN
  • 331
  • 1
  • 7
  • Any thread is able to set the `done` variable to something else at any time, it will just not be visible (or maybe become visible at some time, but without any guarantee) to other threads unless you use the `volatile` keyword. Also what I just found out that any more or less blocking operation actually causes the change to become visible ... for example by simple adding a `System.out.print()` to the loop – Felix Oct 31 '19 at 00:13
  • Yeah me too. I tried printing something inside it worked. But it doesn't work always :P Sometimes, *Sometimes* you will notice that it follows the same convention and the program runs endlessly. – AmN Oct 31 '19 at 00:17