Improper publication of Java Object Reference
You can imagine creation of an object has a number of non-atomic functions. First you want to initialize and publish Holder. But you also need to initialize all the private member fields and publish them.
Well, the JMM has no rules for the write and publication of the holder
's member fields to happen before the write of the holder
field as occurring in initialize()
. What that means is that even though holder
is not null, it is legal for the member fields to not yet be visible to other threads.
You may end up seeing something like
public class Holder {
String someString = "foo";
int someInt = 10;
}
holder
may not be null but someString
could be null and someInt
could be 0.
Under an x86 architecture this is, from what I know, impossible to happen but may not be the case in others.
So next question may be "Why does volatile fix this?" The JMM says that all writes that happen prior to the volatile store are visible to all subsequent threads of the volatile field.
So if holder
is volatile and you see holder
is not null, based on volatile rules, all of the fields would be initialized.
To safely publish this object, do we have to make holder initialization static and declare it as volatile
Yes, because as I mentioned if the holder
variable is not null then all writes would be visible.
How can the
AssertionError
be thrown?
If a thread notices holder
not to be null, and invokes AssertionError
upon entering the method and reading n
the first time may be 0
(the default value), the second read of n
may now see the write from the first thread.
public class Holder {
private int n;
public Holder(int n) { this.n = n; }
public void assertSanity() {
if (n!=n)
throw new AssertionError("This statement is false");
}
}
Say one thread creates an instance of Holder
, and passes the reference to another thread, which calls assertSanity
.
The assignment to this.n
in the constructor occurs in one thread. And two reads of n
occur in another thread. The only happens-before relation here is between the two reads. There is no happens-before relation involving the assignment and any of the reads.
Without any happens-before relations, statements can be reordered in various ways, so from the perspective of one thread, this.n = n
can occur after the constructor has returned.
This means that the assignment can appear to occur in the second thread after the first read and before the second, resulting in inconsistent values. The can be prevented by making n
final, which guarantees that the value is assigned before the constructor finishes.