Search code examples
javamultithreadingsingletoneager-loadinglazy-initialization

Singleton with or without holder = lazy vs eager initialisation?


Is this correct:

  • Using a singleton with a holder gives lazy initialisation because the class SingletonHolder is only initialised when Singleton.getInstance() is run. This relies on SingletonHolder only ever being referenced inside Singleton.getInstance(). It's thread safe because the class loader takes care of synchronisation.
  • Using a singleton without the holder is eager initialisation because as soon as Java comes across code that references Singleton, all its static fields are resolved. It's also thread safe because the class loader takes care of synchronisation.

Singleton with a holder.

public class Singleton {
   private static class SingletonHolder {
      private static final Singleton INSTANCE = new Singleton();
   }
   public static Singleton getInstance() {
      return SingletonHolder.INSTANCE;
   }
   private Singleton(){ }
}

Singleton without a holder.

public class Singleton{
   private static final Singleton INSTANCE = new Singleton();
   public static Singleton getInstance(){
      return INSTANCE;
   }
   private Singleton(){ }
}

Update in response to @jan's suggestion that this is a duplicate of What is an efficient way to implement a singleton pattern in Java?. I disagree. I am not asking what is the best way to do it: I am only asking what makes these two specific implementations lazy vs eager loading. Answers like xyz's broadly address lazy vs eager, but not by contrasting the two examples I was trying to examine (or with the same keywords which is why it never came up in my initial searches).


Solution

  • In response to @Sriram, here is my test to prove which is eager vs lazy loading.

    Lazy loading with a holder

    public class Singleton {
       private static class SingletonHolder {
          static {
             System.out.println("In SingletonHolder static block.");
          }
          private static final Singleton INSTANCE = new Singleton();
       }
    
       public static Singleton getInstance() {
          System.out.println("In getInstance().");
          return SingletonHolder.INSTANCE;
       }
    
       private Singleton() {
          System.out.println("In constructor.");
       }
    
       private void doSomething() {
          System.out.println("Singleton working.");
       }
    
       public static void main(String[] args) {
          System.out.println("Start of main.");
          Singleton.getInstance().doSomething();
          System.out.println("End of main.");
       }
    }
    

    The output shows that the main method starts before getInstance() is called, thus lazily loaded.

    Start of main.
    In getInstance().
    In SingletonHolder static block.
    In constructor.
    Singleton working.
    End of main.
    

    Eager loading without a holder

    public class Singleton {
    
       static {
          System.out.println("In Singleton static block.");
       }
    
       private static final Singleton INSTANCE = new Singleton();
    
       public static Singleton getInstance() {
          System.out.println("In getInstance().");
          return INSTANCE;
       }
    
       private Singleton() {
          System.out.println("In constructor.");
       }
    
       private void doSomething() {
          System.out.println("Singleton working.");
       }
    
       public static void main(String[] args) {
          System.out.println("Start of main.");
          Singleton.getInstance().doSomething();
          System.out.println("End of main.");
       }
    
    }
    

    The output shows that the main method starts after the getInstance() method is called, thus eagerly loaded.

    In Singleton static block.
    In constructor.
    Start of main.
    In getInstance().
    Singleton working.
    End of main.