When to Use Which Approach
Use Eager Initialization When
The singleton is always needed. The cost of creation is low. You want simplicity. Class loading guarantees thread safety. No volatile, no DCL, no complexity.
Use Holder Pattern (Java) When
You want lazy initialization without synchronization overhead. The singleton might not be used in every execution path. This is the sweet spot for most Java applications.
Use Enum Singleton (Java) When
You need protection against serialization and reflection attacks. You want the simplest possible implementation. The singleton does not need to extend another class.
Use DCL When
You need maximum control over initialization timing. You are working in a language without better alternatives. You understand the memory model implications. In practice, this is rare.
Use Static Local (C++11+) When
You are writing modern C++. You want lazy initialization. You want the compiler to handle synchronization. This is the default choice for C++ singletons today.