Creational - Singleton
Multiple ways to implement singleton design pattern, but what should you use?

Imagine there’s only one cookie jar in your house. No matter how many times you ask for a cookie, your parents always send you to the same jar—the ONE and ONLY COOKIE JAR. If all your friends come over, everyone has to share from the same jar too. No matter what, there’s just one magic cookie jar!
If anyone tries to sneak a second cookie jar into the house… the cookie police (your parents!) say “Nope! Only one allowed!”
So, a singleton in computer code is just like this: there can never be two cookie jars, and everyone always gets cookies from the one special jar!
Singleton is simply a class which is instatiated once per JVM initialisation.There are many ways to achieve it.
Eager initialisation
public class EagerSingleton {
// instance created immediately
private static volatile EagerSingleton instance = new EagerSingleton();
// private constructor
private EagerSingleton() {
}
public static EagerSingleton getInstance() {
return instance;
}
}Advantage:
Rely on JVM to create singleton instance
JVM guaraentees that the instance will be created before any thread access the instance variable.
inherently thread-safe
Drawback:
Instance is created irrespective of it is required in runtime or not
This wastes resources
if singleton instance creation is a heavy process
or instance is never used by the client application
Then what's the better approach ?
Lazy Initialization
Lazy initialization is a concept where the Singleton instance is not created until it is needed (on the first call to getInstance()), rather than at class loading time.
public final class LazySingleton {
private static volatile LazySingleton instance = null;
// private constructor
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance == null) { //T1 and T2 at same time
synchronized (LazySingleton.class) { // suppose T1 enters, then T2
instance = new LazySingleton(); // inst1, inst2
}
}
return instance;
}
}Drawback:
Suppose there are two threads T1 and T2. Both comes to create instance and execute instance==null, now both threads have identified instance variable to null thus assume they must create an instance. They sequentially goes to synchronized block and create the instances. At the end, we have two instances in our application.
Then what's the solution?
Double Checked Locking
public class LazySingleton {
private static volatile LazySingleton instance = null;
// private constructor
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance == null) { // Both T1 and T2 at same time
synchronized (LazySingleton.class) { // Suppose T1 enters first
// Double check
if (instance == null) {
instance = new LazySingleton(); // inst1
}
} // Turn for T2
}
return instance;
}
}Please ensure to use Volatile keyword with instance variable otherwise you can run into out of order write error scenario, where reference of instance is returned before actually the object is constructed i.e. JVM has only allocated the memory and constructor code is still not executed. In this case, your other thread, which refer to uninitialized object may throw null pointer exception and can even crash the whole application.
Though it's a popular solution, but there is an alternative approach to get rid of synchronization overhead.
Bill pugh Solution
// Instead of intializing immediately,
//instance will be created using an inner class
public class BillPughSingleton {
// private constructor
private BillPughSingleton() {}
// inner class
private static class LazyHolder {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingletongetInstance() {
return LazyHolder.INSTANCE;
}
}Advantage:
Lazy Initialization : As you can see, until we need an instance, the LazyHolder class will not be initialized until required and you can still use other static members of BillPughSingleton class.
Thread Safety : This method is inherently thread-safe without requiring synchronized blocks.
Class loading in Java is thread-safe by default, so the instance creation is safe in multithreaded environments.
No Synchronization Overhead: There is no performance cost of synchronization in the
BillPughSingletongetInstance()method.
Now, it seems, we are good with singleton pattern using Lazy Initialization. If you think so, then check below code
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class ClientHackerOne {
public static void main(String[] args) throws IOException, ClassNotFoundException {
createMultipleInstanceUsingSerialization();
}
// similar code for bill pugh solution as well
private static void createMultipleInstanceUsingSerialization() throws IOException, ClassNotFoundException {
DLazySingleton instance1 = DLazySingleton.getInstance();
ObjectOutputStream objectOutputStream = new ObjectOutputStream(Files.newOutputStream(Paths.get("tmp.obj")));
objectOutputStream.writeObject(instance1);
objectOutputStream.close();
ObjectInputStream objectInputStream = new ObjectInputStream(Files.newInputStream(Paths.get("tmp.obj")));
DLazySingleton instance2 = (DLazySingleton) objectInputStream.readObject();
objectInputStream.close();
System.out.println("Object1 : " + instance1.hashCode()); //1775282465
System.out.println("Object2 : " + instance2.hashCode()); //1604839423
File file = new File ("tmp.obj");
file.deleteOnExit();
}
}
You can see, the hashcode of the objects are different. This means the objects are different. Hence Singleton pattern is violated.
To solve this, use implement readResolve() method in LazySingletonClass
//inside Singleton class
protected Object readResolve() {
return instance;
}
//This method ensures that during deserialization,
//the existing singleton instance is returned rather than creating a new object.Now you can run ClientHackerOne class to check the hashcodes of the objects.
Now, I think, we are good enough for Singleton Pattern.
Wait!!! Check below class
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class ClientHackerTwo {
public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException {
createMultipleInstanceUsingReflection();
}
private static void createMultipleInstanceUsingReflection() throws InvocationTargetException, InstantiationException, IllegalAccessException {
Constructor<?>[] declaredConstructors = DLazySingleton.class.getDeclaredConstructors();
Constructor<?> constructor = declaredConstructors[0];
constructor.setAccessible(true);
DLazySingleton instance1 = (DLazySingleton) constructor.newInstance();
DLazySingleton instance2 = DLazySingleton.getInstance();
System.out.println("Object1 : " + instance1.hashCode()); //1067040082
System.out.println("Object2 : " + instance2.hashCode()); //1706377736
}
}Again, the objects are with different hashcodes.
To solve this use Enum
Enum
provide implicit support for thread safety and only one instance is guaranteed.Recommended in Effective Java
public enum EnumSingleton {
INSTANCE;
public void someMethod(String param) {
// some class member
}
}To Test the singleton pattern use below code in ClientHackerTwo
EnumSingleton singleton1 = EnumSingleton.INSTANCE;
EnumSingleton singleton2 = EnumSingleton.INSTANCE;
System.out.println("Object1 : " + singleton1.hashCode()); //1804094807
System.out.println("Object2 : " + singleton2.hashCode()); //1804094807A classic example of the singleton pattern in the JDK is the java.lang.Runtime class.
Only one Runtime object can exist in a Java application; you can’t create another instance.
You get the single instance by calling
Runtime.getRuntime().This object lets you interact with the environment in which the application is running (like running external processes, reading environment variables, etc.).
Runtime runtime = Runtime.getRuntime();Conclusion
Singleton Pattern can be achieved by 3 ways
Eager Initialization - JVM way, thread safe
Lazy Initialization - Lazily initialized,
go for double checking approach with readResolve()
or go for bill pugh solution with readResolve()
Enum for simple usecases (recommended)
Last updated
Was this helpful?