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?
"... 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