Reputation: 59
This is an excerpt from book "Java Concurrency in Practice":
// Unsafe publication
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
... Because of visibility problems, the Holder could appear to another thread to be in an inconsistent state, even though its invariants were properly established by its constructor! This improper publication could allow another thread to observe a partially constructed object. ...
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.");
}
}
... But far worse, other threads could see an up-to-date value for the holder reference, but stale values for the state of the Holder. ...
To make things even less predictable, a thread may see a stale value the first time it reads a field and then a more up-to-date value the next time, which is why assertSanity can throw AssertionError. ... the Object constructor first writes the default values to all fields before subclass constructors run. It is therefore possible to see the default value for a field as a stale value.
My question:
It seems that there are only two scenarios in which assertSanity() can throw AssertionError - when "Holder" instance is in instantiation process and default value for "n" is not yet set on "42".
Java will put partially created object in "holder" reference before constructor exits (before constructor initialize "n" field). Another thread will try to call "assertSanity" on this partially created object. Therefore "n != n" operation(s) has to be long enough for AssertionError to occur.
When locally cached "holder" suddenly becomes visible while assertSanity() is in progress.
Is there any other scenario ?
Thanks to all !
Upvotes: 2
Views: 173
Reputation: 3273
You can't really think in terms of "this happens after that", because of reordering. For example
Java will put partially created object in "holder" reference before constructor exits (before constructor initialize "n" field)
Actually it may happen that one thread observes that the constructor has exited and the object has been initialized, but the other thread may see the reference (so it also thinks that the constructor has exited), but the object's field hasn't been initialized for this thread.
So, things get really unpredictable, because without proper synchronization different threads may observe state changes in different orders or not see at all. It's almost impossible to reason about all the possible scenarios here:(
I would strongly recommend reading the "Java Memory Model" part of the "Java Concurrency in Practice" book.
Upvotes: 1