The intent
Double checked locking tries to make lazy initialization cheap: check whether the instance exists without a lock, and only take the lock if it appears null. The idea is to avoid the lock on the common path after the object is created.
Why it is broken
The naive version is unsafe because object construction is not atomic with the pointer assignment. A thread may publish a non null reference to an object whose fields are not yet visible to other threads. Two reorderings cause this:
- The constructor writes can be reordered after the reference store.
- Another core can read the reference but stale field values.
A second thread takes the fast path, sees a non null pointer, and uses a partially constructed object.
Correct versions
- Mark the field volatile or use an acquire and release barrier so the publish is ordered after construction.
- Prefer language idioms: a static holder class initialized by the class loader, or a thread safe lazy primitive.
Key idea
Double checked locking breaks because a reference can be published before the object is fully built. Use a volatile field or a holder idiom so construction happens before the read.