From the book Java Concurrency In Practice:
To publish an object safely, both the reference to the object and the object’s state must be made visible to other threads at the same time. A properly constructed object can be safely published by:
- Initializing an object reference from a static initializer;
- Storing a reference to it into a volatile field or AtomicReference;
- Storing a reference to it into a final field of a properly constructed object; or
- Storing a reference to it into a field that is properly guarded by a lock.
My question is:
Why does the bullet point 3 have the constrain:"of a properly constructed object", but the bullet point 2 does not have?
Does the following code safely publish the map instance? I think the code meets the conditions of bullet point 2.
public class SafePublish {
volatile DummyMap map = new DummyMap();
SafePublish() throws InterruptedException {
new Thread(new Runnable() {
@Override
public void run() {
// Safe to use 'map'?
System.out.println(SafePublish.this.map);
}
}).start();
Thread.sleep(5000);
}
public static void main(String[] args) throws InterruptedException {
SafePublish safePublishInstance = new SafePublish();
}
public class DummyMap {
DummyMap() {
System.out.println("DummyClass constructing");
}
}
}
The following debug snapshot pic shows the map instance is null at the time of the execution is entering the construction of SafePublish. What happens if another thread now trying to read the map reference? Is it safe to read?

It's because
finalfields are guaranteed to be visible to other threads only after the object construction while the visibility of writes tovolatilefields are guaranteed without any additional conditions.From jls-17, on
finalfields:on
volatilefields:Now, regarding your specific code example, JLS 12.5 guarantees that field initialization occurs before the code in your constructor is executed (see steps 4 and 5 in JLS 12.5, which is a bit too long to quote here). Therefore, Program Order guarantees that the constructor's code will see
mapinitialized, regardless of whether it'svolatileorfinalor just a regular field. And since there's a Happens-Before relation before field writes and the start of a thread, even the thread you're creating in the constructor will seemapas initialized.Note that I specifically wrote "before code in your constructor is executed" and not "before the constructor is executed" because that's not the guarantee JSL 12.5 makes (read it!). That's why you're seeing null in the debugger before the first line of the constructor's code, yet the code in your constructor is guaranteed to see that field initialized.