Should int a
in this case be volatile to guarantee visibilty between threads?
private volatile static int a = 0;
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
a = 10;
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(a);
}
});
t1.start();
t2.start();
}
Output
10
Could you explain yourself a bit further on "happens-before"?
The most important thing to remember about "happens before" is that it's a transitive relation. That means, if the Java Language Spec (JLS) promises that A "happens before" B, and it promises that B "happens before" C, then you can infer a promise that A "happens before" C.
The JLS says that a write to some volatile
variable "happens before" a subsequent read of the same variable.
Well Duh! Sounds obvious, doesn't it?
But it's not obvious because the JLS does not give the same guarantee for a non-volatile variable. If processor A writes the value 7 to a non-volatile int, and then some time later processor B writes 5, the JLS does not guarantee that some long time later, the final value of the variable will be 5. Processor B will see 5 (that's a different "happens before" promise, see below). Processor A could see 5 or 7, and any other processor could see 5 or 7 or whatever value the variable had initially (e.g., 0).
How the volatile
promise helps
Suppose we have
volatile boolean flag = false;
/*non-volatile*/ int i = 0;
Suppose thread A does this:
i = 7;
flag = true;
And suppose thread B does this:
if (flag) {
System.out.println(i);
}
else {
System.out.println("Bleah!");
}
Thread B could print "7", or it could print "Bleah!" but because of the "happens before" guarantee, we absolutely know that thread B will never print "0". Why not?
Thread A set i = 7
before it set flag = true
. The JLS guarantees that if a single thread executes one statement before it executes a second statement, then the first statment "happens before" the second statement. (That sounds stupendously obvious, but again, it shouldn't. A lot of things having to do with threads are not obvious.)
Thread B tests flag
before it prints i
. So *IF* thread A previously set flag=true
then we know that i
must equal 7
: Transitivity: i=7
"happens before" flag=true
, and the write to volatile flag
, IF IT HAPPENED AT ALL, "happens before" a read of the same flag
.
IF IT HAPPENED AT ALL
Data Races and Race Conditions
The biggest thing to remember is that when the JLS promises that A "happens before" B, they are not saying that A always actually does happen before B: They are saying that you can depend on that transitive relationship. They are saying that if A actually did happen before B, then all of the things that "happens before" A must also have actually happened before B.
The program can print "Bleah!" because nothing prevents thread B from testing the flag
before thread A sets it. Some people call that a "data race." The two threads are "racing" to see which one gets to the flag
first, and the outcome of the program depends on which thread wins that race.
When the correctness of a program depends on the outcome of a data race, some of us call that a "race condition," and that's a real headache. There's no guarantee that a program with a race condition won't do the right thing a thousand times during your testing, and then do the wrong thing when it matters most for your customer.