Consider the following program:
import java.util.concurrent.TimeUnit; public class StopThread { public static boolean stopRequested; public static void main(String[] args) throws InterruptedException { Runnable task = new Runnable() { @Override public void run() { int i = 0; while (!stopRequested) { i++; System.out.println(i); } System.out.println("Stopping the thread!!"); } }; Thread backgroundThread = new Thread(task); backgroundThread.start(); TimeUnit.SECONDS.sleep(5); stopRequested = true; } }
- Here the
stopRequested
is not declared as volatile – so ideally the threadbackgroupdThread
must not stop – and execute endlessly - But when running this locally – the thread
backgroundThread
is gracefully shutting down with the message: “Stopping the thread!!”.
Are all the updates by the main() thread to the shared variable stopRequested
visible to the other threads? even without the use of volatile
keyword?
Advertisement
Answer
The Java Language Specification does not guarantee this outcome.
In the absence of a synchronization action (such as a volatile
write with a subsequent read), the write does not happen-before the read, and is therefore not guaranteed to be visible.
That is, the read may see the old value or it may see the new one; either outcome is permitted by the Java Memory Model.
To see how narrow the gap is, try removing the printing from the loop:
while (!stopRequested) { i++; }
Executed on
openjdk version "14" 2020-03-17 OpenJDK Runtime Environment (build 14+36-1461) OpenJDK 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)
this code does not terminate. The salient difference is that the loop body become less complex, causing the JIT to apply additional optimizations 🙂
As you can see, the behavior of incorrectly synchronized programs is unpredictable, and can change given the slightest provocation. If you want to write robust multi threaded code, you should therefore prove your code correct with respect to the specification rather than relying on testing.