# Double-Checked Locking
A design pattern that reduces lock overhead by testing the locking criterion without acquiring the lock first. Most commonly used for thread-safe lazy initialization (Singleton pattern).
## The Problem
Naive thread-safe lazy initialization acquires a lock every time:
```java
// Correct but slow — lock acquired on every call
class Singleton {
private static Singleton instance;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
```
## The Solution
```java
class Singleton {
private static volatile Singleton instance;
public static Singleton getInstance() {
if (instance == null) { // First check (no lock)
synchronized (Singleton.class) {
if (instance == null) { // Second check (with lock)
instance = new Singleton();
}
}
}
return instance;
}
}
```
1. **First check** — if instance exists, return immediately without locking
2. **Acquire lock** — only if first check fails
3. **Second check** — another thread may have created the instance between check and lock
## Why `volatile` Is Critical
Without `volatile`, double-checked locking is **broken** in Java (prior to Java 5). The JVM may reorder `instance = new Singleton()` as:
1. Allocate memory
2. Assign reference to `instance` (now non-null!)
3. Call constructor
Another thread could see a non-null `instance` that hasn't been fully constructed. `volatile` prevents this reordering.
## In Other Languages
**C++11 (preferred — magic statics):**
```cpp
// Thread-safe by the standard
Singleton& getInstance() {
static Singleton instance;
return instance;
}
```
**Python** — not typically needed because of the GIL.
## Alternatives
- **[[Initialization-on-demand Holder Idiom]]** (Java) — uses class loading guarantees; generally preferred
- **`enum` Singleton** (Java) — inherently thread-safe and serializable
- **`std::call_once`** (C++) — standard library one-time initialization
---
See also: [[Compare-and-Swap]], [[Initialization-on-demand Holder Idiom]]