Safe publication and lazy init
How a reference becomes visible before the object it points to is finished, and the safe fixes.
Open this lesson in the learning hubKey points
- Publishing means letting another thread see a reference. Without a barrier it can see the reference before the constructor finished.
- The classic bug is double-checked locking on a non-volatile field: the reference lands before the fields it points at do.
- Safe ways to publish: a
static finalfield, avolatilefield, a lock, or a concurrent collection. - The holder idiom is the cleanest lazy singleton: class initialisation is lazy and thread-safe, and the JVM does the locking.
- Never let
thisescape its constructor, for instance by registering a listener before the object is complete.
Example
public class Main {
// 1. The holder idiom: lazy, thread-safe, and the JVM does the locking for you.
static final class Holder {
static final int[] DATA = { 1, 2, 3 };
}
// 2. Double-checked locking is only correct when the field is volatile.
private static volatile int[] cached;
static int[] cached() {
int[] local = cached; // one read of the volatile field
if (local == null) {
synchronized (Main.class) {
local = cached;
if (local == null) { cached = local = new int[] { 4, 5, 6 }; }
}
}
return local;
}
// 3. final fields are frozen when the constructor ends, so readers see them built.
record Point(int x, int y) { }
// 4. The bug: publishing this before the object is finished.
static class Leaky {
static Leaky escaped;
final int ready;
Leaky() {
escaped = this; // visible now, still half-built
ready = 42; // another thread may not see this yet
}
}
public static void main(String[] args) throws InterruptedException {
Thread[] readers = new Thread[4];
for (int i = 0; i < readers.length; i++) {
readers[i] = new Thread(() -> System.out.println(Thread.currentThread().getName()
+ " holder=" + Holder.DATA.length
+ " dcl=" + cached()[0]
+ " final=" + new Point(1, 2)), "reader-" + i);
readers[i].start();
}
for (Thread t : readers) t.join();
new Leaky();
System.out.println("safe publication : every reader saw a fully built value");
System.out.println("leaky escape : ready=" + Leaky.escaped.ready + " here, but not guaranteed elsewhere");
}
}
Build it fully, then publish it safely. A half-built object is visible to other threads.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Multithreading course, and every lesson in it is listed on the Multithreading contents page.