Search code examples
javasingletoneager-loadinglazy-initialization

the Difference between Eager Initialization and Lazy Initialization in Singleton Pattern


I find two different ways to implement a singleton pattern, they are Lazy Initialization and Eager Initialization. and the code is:

public class EagerSingleton {
    private static final EagerSingleton instance = new EagerSingleton();

    private EagerSingleton() {
    }

    public static EagerSingleton getInstance() {
        return instance;
    }
}

… and:

public class LazySingleton {
    private static LazySingleton instance;

    private LazySingleton() {
    }

    public static synchronized LazySingleton getInstance() {
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

It is said that the difference between those two ways is that EagerSingleton initializes early during class loading process, and LazySingleton initializes late when it is first used. But I can't see the timing difference because both them have private constructors, which means that you can't create an instance by new and let it without using (which can see the timing difference). The only way is to use the getInstance() method. So when you use this method, the class loading process and getInstance() both happen at this time. So it seems that they are not different.

Can someone explain the difference in this example?


Solution

  • "... It is said that the difference between those two ways is that EagerSingleton initializes early during class loading process, and LazySingleton initializes late when it is first used. ..."

    Correct, the EagerSingleton class will instantiate the instance field during the class initialization.
    Whereas, the LazySingleton class will wait for the getInstance method to be called.

    "... I can't see the timing difference because both them have private constructors, which means that you can't create an instance by new and let it without using (which can see the timing difference). ..."

    You can utilize the class initialization block to evaluate the fields.

    Consider the following.

    public class EagerSingleton {
        private static final EagerSingleton instance = new EagerSingleton();
    
        static {
            System.out.println("EagerSingleton instantiated");
        }
    
        private EagerSingleton() {
        }
    
        public static EagerSingleton getInstance() {
            System.out.println("EagerSingleton returned instance");
            return instance;
        }
    }
    
    public class LazySingleton {
        private static LazySingleton instance;
    
        static {
            System.out.println("LazySingleton not instantiated");
        }
    
        private LazySingleton() {
        }
    
        public static synchronized LazySingleton getInstance() {
            System.out.println("LazySingleton checking instance");
            if (instance == null) {
                System.out.println("LazySingleton instance assigned");
                instance = new LazySingleton();
            }
            System.out.println("LazySingleton returned instance");
            return instance;
        }
    }
    

    Now, create 2 EagerSingleton and 2 LazySingleton objects.

    System.out.println("e1");
    EagerSingleton e1 = EagerSingleton.getInstance();
    System.out.printf("%nl1%n");
    LazySingleton l1 = LazySingleton.getInstance();
    System.out.printf("%ne2%n");
    EagerSingleton e2 = EagerSingleton.getInstance();
    System.out.printf("%nl2%n");
    LazySingleton l2 = LazySingleton.getInstance();
    

    Output

    e1
    EagerSingleton instantiated
    EagerSingleton returned instance
    
    l1
    LazySingleton not instantiated
    LazySingleton checking instance
    LazySingleton instance assigned
    LazySingleton returned instance
    
    e2
    EagerSingleton returned instance
    
    l2
    LazySingleton checking instance
    LazySingleton returned instance