105. Safe Publication & Thread-Safe Object Initialization
Publishing an object reference across threads without proper synchronization exposes partially initialized state; understand the four safe publication idioms guaranteed by the Java Memory Model.
Java 21 (JLS §17.5 Final Field Semantics)Explanation
When a constructor initializes fields, the CPU and JIT compiler are permitted to reorder field writes relative to the reference assignment. If thread A constructs an object and stores the reference into an unsynchronized shared field, thread B may observe the reference before the constructor's field writes have flushed to main memory. Thread B sees a non-null object whose fields have default values (e.g. 0 or null)! This is called unsafe publication.
The Java Memory Model provides four guaranteed Safe Publication idioms: 1. Initialize in a static initializer: public static final Helper helper = new Helper();. Class initialization <clinit> is thread-safe and protected by JVM class loading locks. 2. Make fields final: Under JLS §17.5, the JVM guarantees that once a constructor finishes, all final fields are visibly frozen to any thread that reads the object reference without needing any synchronization. 3. Store reference into a volatile or AtomicReference: The volatile write creates a happens-before edge with subsequent volatile reads. 4. Guard publication with a lock or synchronized block: Acquiring and releasing monitor locks flushes CPU caches and enforces memory visibility.
Example
public class SafePublicationPatterns {
// 1. Immutable with final fields: 100% thread-safe publication!
public static final class ImmutableHolder {
private final int value;
private final String label;
public ImmutableHolder(int value, String label) {
this.value = value;
this.label = label;
// JMM guarantees value and label are visible to any thread
// as soon as the reference to ImmutableHolder is observed!
}
public int getValue() { return value; }
public String getLabel() { return label; }
}
// 2. Safe lazy publication via volatile
private volatile ImmutableHolder holder;
public void publishSafely(int val, String lbl) {
this.holder = new ImmutableHolder(val, lbl); // volatile write barrier
}
public ImmutableHolder getHolder() {
return this.holder; // volatile read barrier
}
}Sources
- https://docs.oracle.com/javase/specs/jls/se21/html/jls-17.html#jls-17.5 — JLS §17.5: Final Field Semantics
- https://shipilev.net/blog/2014/safe-public/ — Aleksey Shipilëv: Safe Publication and Safe Initialization