Java Singleton Pattern: Class Design with Examples

Singleton pattern enables an application to create the one and only one instance of a Java class per JVM, in all possible scenarios.

The singleton pattern has been debated long enough in the Java community regarding possible approaches to make any class singleton. Still, you will find people not satisfied with any solution you give. They cannot be overruled either. In this post, we will discuss some good approaches and will work towards our best possible effort.

Singleton term is derived from its mathematical counterpart. Singleton pattern helps to create only one instance per context. In Java, one instance per JVM.

Let’s see the possible solutions to create singleton objects in Java.

1. Singleton using Eager Initialization

This is a solution where an instance of a class is created much before it is actually required. Mostly it is done on system startup.

In an eager initialization singleton pattern, the singleton instance is created irrespective of whether any other class actually asked for its instance or not. This is done usually using a static variable as these get initialized at the application startup, always.

public class EagerSingleton {

	private static volatile EagerSingleton instance = new EagerSingleton();

	// private constructor
	private EagerSingleton() {
	}

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

The above method works fine, but it has one drawback. The instance is created irrespective of it is required in runtime or not. If the instance is not a big object and you can live with it being unused, this is the best approach.

Let’s solve the above problem in the next method.

2. Singleton using Lazy Initialization

In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process, until the first time it is needed.

In the context of the singleton pattern, lazy initialization restricts the creation of the instance until it is requested for first time.

Lets see this in code:

public final class LazySingleton {

	private static volatile LazySingleton instance = null;

	// private constructor
	private LazySingleton() {
	}

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

On the first invocation, the getInstance() method will check if the instance is already created using the instance variable. If there is no instance i.e. the instance is null, it will create an instance and will return its reference. If the instance is already created, it will simply return the reference of the instance.

But, this method also has its own drawbacks. Let’s see how.

Suppose there are two threads T1 and T2. Both come to create the instance and check if “instance==null”. Now both threads have identified instance variable as null thus they both assume they must create an instance. They sequentially go into a synchronized block and create the instances. In the end, we have two instances in our application.

This error can be solved using double-checked locking. This principle tells us to recheck the instance variable again in a synchronized block as given below:

public class LazySingleton {

	private static volatile LazySingleton instance = null;

	// private constructor
	private LazySingleton() {
	}

	public static LazySingleton getInstance() {
		if (instance == null) {
			synchronized (LazySingleton.class) {
				// Double check
				if (instance == null) {
					instance = new LazySingleton();
				}
			}
		}
		return instance;
	}
}

Above code is the correct implementation of the singleton pattern.

Please be sure to use “volatile” keyword with instance variable otherwise you can run into an out of order write error scenario, where reference of an 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 refers to the uninitialized object may throw NullPointerException and can even crash the whole application.

3. Singleton with Bill Pugh Solution

Bill Pugh was the main force behind the Java memory model changes. His principle “Initialization-on-demand holder idiom” also uses the static block idea, but in a different way. It suggests using a static inner class.

public class BillPughSingleton {

	private BillPughSingleton() {
	}

	private static class LazyHolder {
		private static final BillPughSingleton INSTANCE = new BillPughSingleton();
	}

	public static BillPughSingleton getInstance() {
		return LazyHolder.INSTANCE;
	}
}

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.

This is the solution, i will recommend to use. I have used it in my all projects.

4. Singleton using Enum

This type of implementation employs the use of enum. Enum, as written in the Java docs, provided implicit support for thread safety and only one instance is guaranteed. Java enum singleton is also a good way to have singleton with minimal effort.

public enum EnumSingleton {

	INSTANCE;

	public void someMethod(String param) {
		// some class member
	}
}


5. Best Practice: Add readResolve() to Singleton Instance

By now you must have made your decision about how you would like to implement your singleton. Now let’s see other problems that may arise even in job interviews.

Let’s say your application is distributed and it frequently serializes objects into the file system, only to read them later when required. Please note that de-serialization always creates a new instance.

Let’s understand using an example:

Our singleton class is:

public class DemoSingleton implements Serializable {

	private volatile static DemoSingleton instance = null;

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

	private int i = 10;

	public int getI() {
		return i;
	}

	public void setI(int i) {
		this.i = i;
	}
}

Let’s serialize this class and de-serialize it after making some changes:

public class SerializationTest {

	static DemoSingleton instanceOne = DemoSingleton.getInstance();

	public static void main(String[] args) {
		try {
			// Serialize to a file
			ObjectOutput out = new ObjectOutputStream(new FileOutputStream("filename.ser"));
			out.writeObject(instanceOne);
			out.close();

			instanceOne.setI(20);

			// Serialize to a file
			ObjectInput in = new ObjectInputStream(new FileInputStream("filename.ser"));
			DemoSingleton instanceTwo = (DemoSingleton) in.readObject();
			in.close();

			System.out.println(instanceOne.getI());
			System.out.println(instanceTwo.getI());

		} catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
}

The program output:

20
10

Unfortunately, both variables have different values of the variable “i”. Clearly, there are two instances of our class. So, again we are in the same problem of multiple instances in our application.

To solve this issue, we need to include a readResolve() method in our DemoSingleton class. This method will be invoked when you de-serialize the object. Inside of this method, you must return the existing instance to ensure a single instance application-wide.

public class DemoSingleton implements Serializable {

	private volatile static DemoSingleton instance = null;

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

	protected Object readResolve() {
		return instance;
	}

	private int i = 10;

	public int getI() {
		return i;
	}

	public void setI(int i) {
		this.i = i;
	}
}

Now when you execute the class SerializationTest, it will give you the correct output.

20
20

6. Best Practice: Add SerialVersionUId to Singleton Instance

So far so good. Until now, we have solved both of the problems of synchronization and serialization. Now, we are just one step away from a correct and complete implementation. The only missing part is a serial version ID.

This is required in cases where your class structure changes between serialization and deserialization. A changed class structure will cause the JVM to give an exception in the de-serializing process.

java.io.InvalidClassException: singleton.DemoSingleton; local class incompatible: stream classdesc serialVersionUID = 5026910492258526905, local class serialVersionUID = 3597984220566440782
at java.io.ObjectStreamClass.initNonProxy(Unknown Source)
at java.io.ObjectInputStream.readNonProxyDesc(Unknown Source)
at java.io.ObjectInputStream.readClassDesc(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at singleton.SerializationTest.main(SerializationTest.java:24)

This problem can be solved only by adding a unique serial version ID to the class. It will prevent the compiler from throwing the exception by telling it that both classes are the same, and will load the available instance variables only.

7. Singleton Pattern Examples in JDK Classes

I just thought to add some examples that can be referred to for further study and mentioned in interviews.

  • java.awt.Desktop#getDesktop()
  • java.lang.Runtime#getRuntime()
  • Logger Instances: Many logging frameworks, like Log4j or SLF4J, often implement Singleton patterns for their logger instances to ensure that all parts of the application use the same logger configuration and instance.
  • Java Naming and Directory Interface (JNDI): In Java EE applications, the InitialContext class from the javax.naming package is often implemented as a Singleton. It provides access to the naming and directory services for JNDI.

8. Conclusion

After having discussed so many possible approaches and other possible error cases, I will recommend to you the code template below, to design your singleton class which shall ensure only one instance of a class in the whole application in all the above-discussed scenarios.

public class DemoSingleton implements Serializable {

	private static final long serialVersionUID = 1L;

	private DemoSingleton() {
		// private constructor
	}

	private static class DemoSingletonHolder {
		public static final DemoSingleton INSTANCE = new DemoSingleton();
	}

	public static DemoSingleton getInstance() {
		return DemoSingletonHolder.INSTANCE;
	}

	protected Object readResolve() {
		return getInstance();
	}
}

I hope this post has enough information to help you understand the most common approaches for the singleton pattern and singleton best practices. Let me know your thoughts.

Happy Learning !!

Comments

Subscribe
Notify of
guest
96 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.

Our Blogs

REST API Tutorial

Dark Mode

Dark Mode