JVM & Memory

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