The advantages of Bill Pugh's solution over the presented implementation of lazy initialized singleton are performance related. Consider the next scenario.
The instance is already initialized and two threads concurrently request the instance.
- For lazy initialized implementation, since the method is synchronized, one of the threads will block.
- For Bill Pugh implementation there would be no blocking.
Anyway, this could be (partially) mitigated by implemented the singleton via double checked locking. See the example below.
Double Checked locking singleton:
public final class DoubleCheckedLockingSingleton {
private static volatile DoubleCheckedLockingSingleton instance;
private DoubleCheckedLockingSingleton(){
if(instance!=null)
throw new RuntimeException();
}
public static final DoubleCheckedLockingSingleton getInstance(){
if(instance==null){
synchronized(DoubleCheckedLockingSingleton.class) {
if(instance==null)
instance = new DoubleCheckedLockingSingleton();
}
}
return instance;
}
}
In this case, the performance difference is insignificant. The main difference between a double checked locking implementation and a holder pattern implementation is the reason why it works.
- Double checked locking: threads will block only if they trigger the instance creation concurrently. You have to recheck for
instance == null
inside the synchronized block since two threads could (potentially although highly unlikely - the best kind of bug) swap out between the first if and the synchronized block. You also have to declare the variable volatile in order to leverage the JMM 'happens before' guarantees of volatile (so you are sure the instance==null
check won't return false until the instance is fully initialized).
- Holder pattern: threads will block only if they trigger the instance creation concurrently. All you have to know is that classes in Java are loaded lazily, with lock from JVM (so both locking and visibility are enforced out of the box, with no additional effort from your side).
Personally, I prefer holder pattern over double checked locking since the reasons why it works seem easier to understand (at lest to me).
As a final note, if your requirements allows it (i.e. if you are using a DI framework, such as Spring), the best way to implement a singleton would be to let Spring provide the singleton for you (by using @Component
with the default singleton scope).