5

Using a basic example to illustrate my problem I have 2 near-identical bits of code.

This code causes the while loop to run infinitely.

private boolean loadAsset() {
    new Thread(new Runnable() {

        @Override
        public void run() {

            // Do something
            loaded = true;

        }
    }).start();

    while (!loaded) {
        // System.out.println("Not Loaded");
    }
    System.out.println("Loaded");
    return false;
}

This code however (i.e. doing something in the while loop) causes the loaded variable to be successfully evaluated and allows the while loop to break and method to finish.

private boolean loadAsset() {
    new Thread(new Runnable() {

        @Override
        public void run() {

            // Do something
            loaded = true;

        }
    }).start();

    while (!loaded) {
        System.out.println("Not Loaded");
    }
    System.out.println("Loaded");
    return false;
}

Can anyone explain to me why this is?

Sonoman
  • 3,379
  • 9
  • 45
  • 61
  • Why are you relying on a field value which can be changed by other field? You may have to make whole while block synchronized – Reddy Jul 12 '11 at 13:17

5 Answers5

7

The first loop only "appears" to run infinitely. You're actually running an "active wait", burning 100% of your CPU, such that your OS or JVM can't make a context switch and let the other thread run.

With the System.out.println() on the other hand, there is I/O involved, resulting in a somewhat "inactive wait". The OS or JVM can switch contexts and the other thread starts.

If you'd run your first program for 10 hours, I'm sure the loop would break eventually

Lukas Eder
  • 211,314
  • 129
  • 689
  • 1,509
6

Check that 'loaded' is definitely declared as volatile.

Explanation: if a variable is read and/or written by multiple threads, then you need to take appropriate thread-safety measures. One such thread-safety measure is volatile, which is suitable for primitive values (or object references) which are read or written as 'simple' actions with the value written on a given occasion not depending on the previously read value. For more information, I have an article about volatile on my web site (along with other information about thread-safety generally) that may be of help.

Neil Coffey
  • 21,615
  • 7
  • 62
  • 83
4

If loaded is not volatile, the JIT is free to optimise it by placing it in a register and not loading it from memory every time. In the second case, the loop is too complex for the JIT to assume it doesn't need to load loaded each time.

Note: its is the JIT not the javac compiler which optimises the code.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • 1
    Nice. I was not aware of that... – Lukas Eder Jul 12 '11 at 13:37
  • @Peter Lawrey, Cache is controlled by CPU not JIT, I think `loaded` may be discarded when cache is full, and then be loaded from memory again. Refer to [here](https://stackoverflow.com/questions/37348390/java-jit-compiler-optimizations-is-jit-consistent-in-respect-to-volatile-varia?noredirect=1&lq=1). – gaussclb Dec 22 '19 at 16:10
  • In fact, JIT optimizes the while loop into an infinite loop, see [here](https://stackoverflow.com/a/25425131/8165066). – gaussclb Dec 22 '19 at 16:16
2

Read up on Memory Consistency Errors. Basically, different threads have inconsistent views of what should be the same data. In order to resolve this, read up on Synchronization. Or simply declare loaded as volatile, since its value is only being written by a single thread.

mre
  • 43,520
  • 33
  • 120
  • 170
0

I believe you are experiencing a Busy Wait with the empty loop, which will never sleep. Thus, your Thread to set loaded to true never runs.

Briguy37
  • 8,342
  • 3
  • 33
  • 53