Double Checked Locking
Double checked locking is an optimization for lazy initialization. You want to create an expensive object exactly once, the first time it is needed, while letting later callers read it without paying for a lock.
The naive safe version takes a lock on every access, which is slow once the object exists. Double checked locking adds a fast path:
- Check whether the field is already set without locking
- If it is set, return it immediately
- If not, acquire the lock, check again, and only then create the object
The second check matters because two threads may both pass the first check before either takes the lock. Inside the lock only one wins and builds the object, while the other sees the now set field and reuses it.
The famous trap is memory visibility. Without proper ordering, one thread can publish a reference to an object whose fields are not yet fully written, so another thread sees a half built instance. The fix is to mark the field volatile, which forbids the reordering and guarantees that a thread reading a non null reference also sees a fully constructed object.
Modern languages often prefer simpler alternatives such as an initialization on demand holder or a language level lazy primitive, which give the same laziness without the subtlety.
Key idea
Double checked locking skips the lock on the common path but needs a volatile field so a published reference always points to a fully constructed object.