According to:
http://www.ibm.com/developerworks/library/j-jtp03304/
Under the new memory model, when thread A writes to a volatile variable V, and thread B reads from V, any variable values that were visible to A at the time that V was written are guaranteed now to be visible to B
And many places on the internet state that the following code should never print “error”:
public class Test {
volatile static private int a;
static private int b;
public static void main(String [] args) throws Exception {
for (int i = 0; i < 100; i++) {
new Thread() {
@Override
public void run() {
int tt = b; // makes the jvm cache the value of b
while (a==0) {
}
if (b == 0) {
System.out.println("error");
}
}
}.start();
}
b = 1;
a = 1;
}
}
b should be 1 for all the threads when a is 1.
However I sometimes get “error” printed. How is this possible?
Update:
For anyone interested this bug has been addressed and fixed for Java 7u6 build b14. You can see the bug report/fixes here
Original Answer
When thinking in terms of memory visibility/order you would need to think about its happens-before relationship. The important pre condition for
b != 0is fora == 1. Ifa != 1then b can be either 0 or 1.Once a thread sees
a == 1then that thread is guaranteed to seeb == 1.Post Java 5, in the OP example, once the
while(a == 0)breaks out b is guaranteed to be 1Edit:
I ran the simulation many number of times and didn’t see your output.
What OS, Java version & CPU are you testing under?
I am on Windows 7, Java 1.6_24 (trying with _31)
Edit 2:
Kudos to the OP and Walter Laan – For me it only happened when I switched from 64 bit Java to 32 bit Java, on (but may not be excluded to) a 64 bit windows 7.
Edit 3:
The assignment to
tt, or rather the staticget ofbseems to have a significant impact (to prove this remove theint tt = b;and it should always work.It appears the load of
bintottwill store the field locally which will then be used in the if coniditonal (the reference to that value nottt). So ifb == 0is true it probably means that the local store tottwas 0 (at this point its a race to assign 1 to localtt). This seems only to be true for 32 Bit Java 1.6 & 7 with client set.I compared the two output assembly and the immediate difference was here. (Keep in mind these are snippets).
This printed "error"
And
This did not print "error"
In this example the first entry is from a run that printed "error" while the second was from one which didnt.
It seems that the working run loaded and assigned
bcorrectly before testing it equal to 0.While the run that printed "error" loaded the cached version of
%edxFor those who have more experience with assembler please weigh in 🙂
Edit 4
Should be my last edit, as the concurrency dev’s get a hand on it, I did test with and without the
int tt = b;assignment some more. I found that when I increase the max from 100 to 1000 there seems to be a 100% error rate whenint tt = bis included and a 0% chance when it is excluded.