Reputation: 375
In the book Java Concurrency in Practice by Brian Goetz et al.:
If you do not ensure that publishing the shared reference happens-before another thread loads that shared reference, then the write of the reference to the new object can be reordered (from the perspective of the thread consuming the object) with writes to its fields. In that case, another thread could see an up-to-date value for the object reference but out-of-date values for some or all of that object's state-a partially constructed object.
Does this mean that: in the thread publishing the object, the write of the reference to the new object is not reordered with writes to its fields; the write to its fields happens before the write of the reference. However, that publishing thread may flush the updated reference to main memory before it flushes the updated object fields. Therefore, the thread consuming the object may see a non-null reference for the object, yet see outdated values for the object fields? And in that sense, the operations are reordered for the consuming thread.
Upvotes: 2
Views: 330
Reputation: 1587
in the thread publishing the object, the write of the reference to the new object is not reordered with writes to its fields; the write to its fields happens before the write of the reference.
Yes. Because in one single thread this process happens in Program Order which doesn't allow reordering: "If x and y are actions of the same thread and x comes before y in program order, then hb(x, y)." (https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5). We may rephrase you a bit: "the write of the reference to the new object is not reordered with writes to its fields", which means that if you read the reference to an object, it is guaranteed that you will read all its fields consecutively.
the thread consuming the object may see a non-null reference for the object, yet see outdated values for the object fields?
Yes, it may when you publish the object in unsafe manner, without appropriate HB edges implemented with memory barriers. Literally speaking, in absence of the HB/membars you get undefined behavior. This means that in other thread you can see/read anything (except out-of-thin-air (OoTA) values, explicitly forbidden by JMM). Safe publication makes all the values written before the publication visible to all readers that observed the published object. There are few most popular and simple ways to make the publication safe:
Publish the reference through a properly locked field (https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5)
Use the static initializer to do the initializing stores (http://docs.oracle.com/javase/specs/jls/se8/html/jls-12.html#jls-12.4)
Publish the reference via a volatile field (https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4.5), or as the consequence of this rule, via the AtomicX classes
Initialize the value into a final field, which leads to the freeze action (https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5).
You also can use other actions which produces HBs like Thread.start() etc., but my day-to-day favorites are:
Upvotes: 1
Reputation: 27190
Yes.
The answer to your question is right there in the paragraph that you quoted, and you seem to echo the answer in your question.
One comment though: You said that, "[the] publishing thread may flush the updated reference to main memory before it flushes the updated object fields." If you're talking about Java code, then it's best to stick with what is written in the Java Language Specification (JLS).
The JLS tells you how a Java program is allowed to behave. It says nothing about "main memory" or "caches" or "flushing." It only says that without explicit synchronization, the updates that one thread performs in a certain order on two or more variables may seem to have happened in a different order when viewed from the perspective of some other thread. How or why that can happen is "implementation details."
Upvotes: 1