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.
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.
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.
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.
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.
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.
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:
i
happens before write of flag
due to the program order ruleflag
happens before read of flag
due to volatile variable rule (on the hardware level this is a task for cache coherence).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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With