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?
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With