Let's first take a look at one simple Java thread code snippet which is supposed to exit the while loop after the first loop run.
public class StopThread {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!stopRequested) {
i++;
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
But the truth is that the loop may not exit and keep running. The reason is that the updated stopRequested value will be in the main memory while the value of stopRequested for the thread is still in local stack which is not synced up with main memory. In this case, volatile may be used to enforce the sync up between main memory and the thread so that the thread always gets the updated value.
However, the topic of this post is not about volatile. It's another question. Can the while loop be exited with other methods?
Let's take a look at below code.
public class StopThread {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!stopRequested) {
i++;
System.out.println(""+i);
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
The only change is that System.out.println(""+i); added after i++;, this simple change will make the while loop exit after one loop. Some people may think that it's because there is synchronized keyword in System.out.println() source code which would sync up the value of stopRequested. But this understanding is incorrect since synchronized is only for the variables inside the synchronized block. The stopRequested variable is outside the synchronized block.
What happens indeed would be that JVM will try its best to ensure the visibility of variables from main memory to other memory areas. i.e, JVM would try to update the variable as long as there are free CPU clocks. The difference of this behavior and volatile is that volatile will force to ensure the visibility of the variable while the default behavior of JVM is it tries its best to do this but not guaranteed. That's why volatile keyword is needed if want an anticipated behavior.
In this example, System.out.println() has synchronized keyword in its implementation which may block the execution while waiting for other threads execution, in the meantime, CPU can be try to sync up the variables in memory. Though this is not guaranteed to be updated. After knowing this, it seems other methods like Thread.sleep() can also make the while loop exit.
public class StopThread {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread = new Thread(new Runnable() {
@Override
public void run() {
int i = 0;
while (!stopRequested) {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(i);
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested = true;
}
}
Note this post is not to tell you that you should use these methods to exit the while loop. it's just to explore the behavior of JVM and the language itself so that you would understand better on what's happening when something unexpected happens. The correct way is still to use volatile whenever you can.