1

I am learning about volatile of Java and my code is like this.

public class Test {
    public static boolean flag = true;

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            while(flag){
                // System.out.println(flag);
            }
            System.out.println("end");
        });
        t1.start();

        Thread.sleep(100);

        Thread t2 = new Thread(()->{
            flag = false;
            System.out.println("changed");
        });
        t2.start();
    }
}

I know if flag doesn't have volatile, the thread would not exist. It's a visibility problem.

However if I write some code in the while loop, like System.out.println("abc"), t1 thread will read the new value and stop the loop.

I know how to use volatile to resolve the visibility problem, so my question is:

Why t1 can read the new value of flag when I write System.out.println("abc") in the while loop?

CPHPython
  • 12,379
  • 5
  • 59
  • 71
MapleTree
  • 41
  • 7
  • **tl;dr** from the duplicate: `System.out.println()` is "special" because it involves synchronization which in many (all?) JVMs has the effect of a very broad read barrier and effectively causes the variable to be re-read from main memory. That is not required by the spec, but is a widely shared implementation detail. – Joachim Sauer Dec 15 '20 at 09:44

1 Answers1

1

When you do:

while(flag){
     // System.out.println(flag);
}

you will have loop with an empty body, which can result in what is know by Spin on field; from source one can read:

Repeatedly reading a non-volatile field within the condition of an empty loop statement may result in an infinite loop, since a compiler optimization may move this field access out of the loop.

However, by adding a statement System.out.println(flag); to your loop you avoid the Spin on field. Notwithstanding, if thread t1 read the field flag from its cache with a value true, the thread will only stop when the cache line that stores that field value gets invalidated, and the thread gets the new update valued flag field (i.e., flag=false) from the main memory.

The volatile ensures, among others, that the threads will read the most update value the flag field. Hence, making the field flag volatile ensure that:

  while(flag){
    // System.out.println(flag);
}

potential compiler optimizations will NOT move the access to the field flag out of the loop.

But if I write some code in the while loop, like System.out.println("abc"), thread t1 will read new value and stop loop.

This is two-folded first by adding the System.out.println("abc") you will avoid the Spin on field problem and second if one looks at the System.out.println code:

   public void println(String x) {
        synchronized (this) {
            print(x);
            newLine();
        }
    }

you can see that there is a synchronized being called, which will increase the likelihood that the thread will be reading the field flag not from the cache but from the main memory.

dreamcrash
  • 47,137
  • 25
  • 94
  • 117