Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Timed while loop not terminating [duplicate]

Running this code, I would expect it to increment the test variable for 5 seconds and then finish.

import java.util.Timer;
import java.util.TimerTask;

public class Test {
    private static boolean running;

    public static void main( String[] args ) {
        long time = 5 * 1000;       // converts time to milliseconds
        long test = Long.MIN_VALUE;
        running = true;

        // Uses an anonymous class to set the running variable to false
        Timer timer = new Timer(); 
        timer.schedule( new TimerTask() { 
            @Override
            public void run() { running = false; }
        }, time );

        while( running ) {
            test++;
        }

        timer.cancel();
        System.out.println( test );
    }
}

However when I run it the program doesn't end (I assume, I have given it a reasonable amount of time). However if I change the while loop to

    while( running ) {
        System.out.println();
        test++;
    }

The program finishes in the expected amount of time (and prints out a lot of lines). I don't understand. Why does this behaviour occur?

like image 879
DenverCoder9 Avatar asked May 27 '15 05:05

DenverCoder9


People also ask

Why does my while loop not end?

The issue with your while loop not closing is because you have an embedded for loop in your code. What happens, is your code will enter the while loop, because while(test) will result in true . Then, your code will enter the for loop. Inside of your for loop, you have the code looping from 1-10.

Why use a while loop?

The while loop in Python is used to iterate over a block of code as long as the test expression (condition) is true. We generally use this loop when we don't know the number of times to iterate beforehand.


3 Answers

According to Java Memory Model there's no guarantee when non-volatile field will be visible from another thread. In your case your main method is JIT compiled and JIT-compiler reasonably assume that as current thread does not modify the running variable in a loop, then we should not bother reading it on every iteration and convert the loop to the infinite one. There's even FindBugs warning regarding this case.

When you add a System.out.println call, seems that JIT compiler cannot inline it, so it cannot be sure that this method does not modify the running variable, thus it switches off the field reading optimization. However that should not be considered as problem solution: it's possible that newer versions of Java will be smarter and optimize your loop even if you have System.out.println inside.

like image 64
Tagir Valeev Avatar answered Oct 25 '22 20:10

Tagir Valeev


Lets get closer into your code...

If you debug your code, it will work. That's because you will only see one thread and no cache. Java can use thread-based cache mechanisms. You need to read and write it into main memory.

So if you use volatile keyword on your running variable, jvm will see it as editable by multiple threads, and it shouldn't be cached.

Therefore in your situation solution is adding volatile to your running variable.

like image 24
Sercan Ozdemir Avatar answered Oct 25 '22 22:10

Sercan Ozdemir


You are updating the running variable in a different thread from your main method. The Java Memory Model is such that you are not guaranteed to see updates to non-volatile variables in different threads.

The easiest solution is to make the variable volatile: this forces the 'current' value of the variable to be checked when the thread accesses it.

Other solutions include using AtomicBoolean instead of boolean, and wrapping access to running in mutually synchronized blocks (i.e. the code that accesses running is synchronized on the same monitor as the code which updates it).

I strongly recommend that you pick up a copy of Java Concurrency In Practice, which describes this problem in detail.

like image 44
Andy Turner Avatar answered Oct 25 '22 20:10

Andy Turner