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 != 0
is fora == 1
. Ifa != 1
then b can be either 0 or 1.Once a thread sees
a == 1
then 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 ofb
seems to have a significant impact (to prove this remove theint tt = b;
and it should always work.It appears the load of
b
intott
will store the field locally which will then be used in the if coniditonal (the reference to that value nottt
). So ifb == 0
is true it probably means that the local store tott
was 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
b
correctly before testing it equal to 0.While the run that printed "error" loaded the cached version of
%edx
For 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 = b
is included and a 0% chance when it is excluded.Based on the extract from JCiP below, I would have thought that your example should never print "error":
You might want to check out a discussion thread on the concurrency interest mailing list on this question: http://cs.oswego.edu/pipermail/concurrency-interest/2012-May/009440.html
It seems like the problem is more easily reproduced with the client JVM (-client).
in My opinion,The Problem acurred due to Lack of Synchronization :
NOTICE : if b=1 heppens before a=1, and a is volatile while b is not, then b=1 actually updates for all threads only after a=1 is finished (according to the quate's logic).
what heppend in your code is that b=1 was first updated for the main process only, then only when the volatile assignment finished, all the threads b's updated. I think that maybe assignments of volatile are not working as atomic operations (needs to point far, and somehow update rest of refernces to act like volatiles) so this would be my guess why one thread read b=0 instead of b=1.
Consider this change to the code, that shows my claim: