Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The visibility of variable which write after volatile variable write

public class Test {
    private static volatile boolean flag = false;
    private static int i = 1;
    public static void main(String[] args) {
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            flag = true;
            i += 1;
        }).start();
        new Thread(() -> {
            while (!flag) {
                if (i != 1) {
                    System.out.println(i);
                }
            }
            System.out.println(flag);
            System.out.println(i);
        }).start();
    }
}

Variable i is written after volatile variable flag, but the code output true 2. It seems the modify of i by the first thread is visible to the second thread.

According to my understanding, variable I should be written before flag, then the second thread can be aware of the change.

like image 307
John Smith Avatar asked Dec 22 '20 16:12

John Smith


People also ask

Which variable is volatile?

A volatile variable is a variable that is marked or cast with the keyword "volatile" so that it is established that the variable can be changed by some outside factor, such as the operating system or other software.

What is the use of volatile variable?

Volatile is a qualifier that is applied to a variable when it is declared. It tells the compiler that the value of the variable may change at any time-without any action being taken by the code the compiler finds nearby. The implications of this are quite serious.

Why variable is called volatile?

A variable should be declared volatile whenever its value can be changed by something beyond the control of the code section in which it appears, such as a concurrently executing thread.

What is the use of volatile keyword when should we use volatile variable in Java?

The volatile modifier is used to let the JVM know that a thread accessing the variable must always merge its own private copy of the variable with the master copy in the memory. Accessing a volatile variable synchronizes all the cached copied of the variables in the main memory.


Video Answer


2 Answers

Accordingly to the language standard (§17.4):

A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable

So informally, all threads will have a view of the must update value of that variable.

However, the volatile clause not only implies ensure visibility guarantee of the target variable but also full volatile visibility guarantee, namely:

Actually, the visibility guarantee of Java volatile goes beyond the volatile variable itself. The visibility guarantee is as follows:

If Thread A writes to a volatile variable and Thread B subsequently reads the same volatile variable, then all variables visible to Thread A before writing the volatile variable, will also be visible to Thread B after it has read the volatile variable.

If Thread A reads a volatile variable, then all all variables visible to Thread A when reading the volatile variable will also be re-read from main memory.

According to my understanding, variable I should be written before flag, then the second thread can be aware of the change.

"All variables visible to the Thread A before writing the volatile variable", it does not refer to operation over those variables.

like image 118
dreamcrash Avatar answered Oct 05 '22 22:10

dreamcrash


Your code suffers from a data-race.

A data race is when there are 2 memory actions to the same address which are not ordered by a happens before relation, and at least one of these actions is a write.

In this case the write to i is the problem.

The write to i, is after the write to the volatile variable flag and hence there is no happens before relation between writing the i and reading i.

If you would write i before you write to the flag, there would be the following happens before relation:

  1. write of i happens before write of flag due to the program order rule
  2. write of flag happens before read of flag due to volatile variable rule (on the hardware level this is a task for cache coherence).
  3. the read of flag happens before the read of i due to program order rule.

Because the happens before relation is transitive, the write of i happens before the read if i.

So like you already indicated, if you move the write of i in front of the write of the flag; the data race is gone.

like image 36
pveentjer Avatar answered Oct 06 '22 00:10

pveentjer