Skip to content
Advertisement

Why is volatile keyword not needed for inter-thread visibility when one of the threads involved is the main() thread?

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 thread backgroupdThread 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.

Advertisement