class C {
Object o;
public void set(Object o){
if(this.o == null){
this.o = o;
}
}
public Object get(){
return o;
}
}
C c = new C();
C c = new C();
Thread#1
Object o1 = c.get(); // 1
Object o2 = c.get(); // 2
Thread#2
c.set(new Object());
Is it possible that o2 == null && o1 != null
? Why?
To make it clear what I mean I edited:
What if we have the following situation:
C c = new C(); // it is given at beginning
1) Object o2 = c.o; // o2 is null. This operation was **reordered** before O `o1 = c.o. The JVM can do it because JMM allows do it.
2) c.o = new Object()` //Thread #2 was executed
3) O o1 = c.o // o1 is not null while o2 is.
It is not possible, despite the fact that you have a data race.
The data race is because your gets and sets around o
aren't synchronized, which means there's no happens-before order with them. You could solve that either by having both methods be synchronized
, or by making o
volatile, or in a few other ways.
Absent synchronization, the JVM is allowed to reorder events as seen by other threads. From Thread1's perspective, you have the following events (with methods inlined for simplicity):
c.o = null; // initial value
o1 = c.o;
o2 = c.o;
c.o = new Object(); // from Thread2
Luckily for you, there are two restrictions that make this work:
cc = null
happens-before all other actions (see JLS 17.4.5 ). o1 = co
happens-before o2 = co
. (Thread2 would not have to see those reads in that order... but it never sees o1
or o2
at all, so there's no problem there.) The first of those means that we can't take the co = null
action and order it after cc = new Object()
. The second means that the reordering you mention at the bottom of your post is not allowed, from Thread1's perspective (and of course, Thread1 is the only thread that sees anything about o1 or o2).
Combining those two restrictions, we can see that if Thread1 ever sees co
be non-null, then it will never see it revert back to null again. If o1 is non-null, then so must o2 be.
Note that this is pretty fickle. For instance, let's say that rather than only setting co
once, Thread2 set it twice:
c.set("one");
c.set("two");
In that case, it would be possible to see o1
be "two" while o2
is "one". That's because the operations there are:
c.o = null; // initial value
o1 = c.o;
o2 = c.o;
c.c = "one"; // from Thread2
c.c = "two"; // from Thread2
The JVM can reorder the items from Thread2 however it sees fit, so long as they don't come before that cc = null
. In particular, this is valid:
c.o = null; // initial value
c.c = "two"; // from Thread2
o1 = c.o;
c.c = "one"; // from Thread2
o2 = c.o;
Removing the data race, by synchronizing the gets and sets to o
, will fix that.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.