top of page
Writer's pictureThe Tech Platform

Singleton Design Pattern with Java

Updated: Apr 5, 2023

Singleton Design Pattern is a creational design pattern that ensures that only one instance of a class exists throughout the entire lifecycle of the application. In this article, we'll discuss the Singleton Design Pattern and its implementation in Java.

Why Use Singleton Design Pattern?

There are several reasons why you might want to use the Singleton Design Pattern in your Java application. Some of the main reasons include:

  1. Resource Management: Sometimes, resources like database connections or threads can be limited. In such cases, using the Singleton Design Pattern ensures that these resources are used efficiently.

  2. Centralized Configuration: In applications where configuration information is centralized, the Singleton Design Pattern can be used to ensure that this information is accessible from a single location.

  3. Consistent State: The Singleton Design Pattern can help to ensure that an object remains in a consistent state throughout the lifetime of the application.

  4. Controlled Access: The Singleton Design Pattern can be used to ensure that only one instance of an object is created, which can help to prevent multiple instances from being created inadvertently.

Implementing Singleton Design Pattern in Java

Let's have a class “TheTech” and we create a reference for this class.

Creating more than one instance means both reference variables will have different values stored. Thus it is “NOT” a Singleton object. Below is the scenario, here c1 and c2 are two reference variables carrying different values.

package SingletonDesign;  
 
public class TheTech {  
 public static void main(String[] args) {  
        TheTech c1 = new TheTech();  
        TheTech c2 = new TheTech();  
    }  
}   

A singleton design pattern is divided into two types,

  1. Eager Loading: creation at load time.

  2. Lazy Loading: the creation of an object whenever needed.

Both eager and lazy design patterns differ in the way of returning the object, if an object is null it returns a new singleton object or other particular objects.

So we are now starting with eager loading and for this, we need to remember some rules for constructing eager loading objects.

Eager loading

To make any class Singleton the first thing that comes to mind is the Singleton keyword but no singleton is a concept in Java that can only be achieved by learning some below-mentioned steps.

  • Default constructor as private.

  • Create a private static class type variable and initialize it with null.

  • Initialise variable(in step 2) to the constructor of class type(here Singleton).

  • Create a public static getObject or getInstance method and return a class object.

Here is an example which implements these rules for object creation,

package SingletonDesign;  
 
class Singleton {  
 //Eager Loading Singleton object 
 //rule 1.2 
 private static Singleton obj = null;  
 
 // rule 1.1 
 private Singleton() {  
 //rule 1.3 
        obj = new Singleton();  
    }  
 
 //rule 1.4 
 public static Singleton getInstance() {  
 return obj;  
    }  
}  
public class TheTech {  
 public static void main(String[] args) {  
        Singleton c1 = Singleton.getInstance();  
        Singleton c2 = Singleton.getInstance();  
    }  
}  

In the above example, the very first step is to create a private default constructor as a private constructor does not allow new objects to be created.

The second step is to create a private static class type variable, this variable is to be static as it’s been used inside the constructor for object creation.

The third step is to create the object using a class-type variable obtained from the second step.

In the fourth and last step, we create a getInstance() method to return the object of the singleton class. Since getInstance() is a static method every time we call will create only one instance of the class.

Point to remember

The above example is called eager loading, when we observe we find some drawbacks in this; the reference object “obj” is of type static, which means this object will be created and is available in memory when the class is loaded, so this is a global variable because whenever a class is loaded that object is available in local memory. That’s the drawback, as, if this object is bulky then it is just a waste of memory and processing power. To overcome this we use the concept of lazy loading.

Note

If we try to make more objects of the Singleton class this is not possible now as one object is created earlier and we can’t get more than one instance in the singleton design pattern.

Lazy loading

To make any class Singleton the first thing that comes to mind is the Singleton keyword but no singleton is a concept in Java that can only be achieved by learning some below-mentioned steps.

  • Default constructor as private.

  • Create a private static class type variable and initialize it with null.

  • Create a public static getObject or getInstance method and return a class object.

Here is an example which implements these rules for object creation,

package SingletonDesign;  
 
class Singleton {  
 // Lazy Loading Singleton 
 //rule 2.2 
 private static Singleton obj;  
 
 // rule 2.1 
 private Singleton() {  
 
    }  
 
 //rule 2.3 
 public static Singleton getInstance() {  
 if (obj == null) {  
            obj = new Singleton();  
        }  
 return obj;  
    }  
}  
public class TheTech {  
 public static void main(String[] args) {  
        Singleton c1 = Singleton.getInstance();  
    }  
}  

This technique overcomes the drawback of eager loading by creating an object at the time of calling the getInstance() method.

In the above example, the very first step is to create a private default constructor as a private constructor does not allow new objects to be created.

The second step is to create a private static class type variable, this variable is to be static as it’s been used inside the constructor for object creation.

In the third and last step, we create a getInstance() method to return the object of the singleton class on the basis of a condition.

Synchronized with Thread in Lazy Loading

To overcome the drawbacks of the above implementation we used. So when we have more than one instance then it will execute the things sequentially and for the first time when we call getInstance() the object is null thus a new object is created but when we call it for the second time we already have an object so it will not be executed and return an older object.

package SingletonDesign;  
 
class Singleton {  
 private static Singleton obj;  
 
 private Singleton() {  
 
    }  
 
 public static synchronized Singleton getInstance() //check 
    {  
 if (obj == null) {  
            obj = new Singleton();  
        }  
 return obj;  
    }  
}  
 
public class TheTech {  
 public static void main(String[] args) {  
        Thread t1 = new Thread(new Runnable() {  
 public void run() {  
                Singleton c1 = Singleton.getInstance();  
            }  
        });  
 
        Thread t2 = new Thread(new Runnable() {  
 public void run() {  
                Singleton c1 = Singleton.getInstance();  
            }  
        });  
 
        t1.start();  
        t2.start();  
    }  
}  

So in the above example, we have two threads, t1 and t2, and both are calling the same method and we start both at the same time.

Using Thread with Lazy Loading (Double Check Locking Technique)

To overcome the drawbacks (time complexity) with the above implementation, we can use the concept of Double-Check Locking; it means we will check object value two times i.e

  1. Simply i.e ( if(obj == null) )

  2. Inside Synchronized Block

To overcome the time complexity by synchronizing with the getInstance() method we can introduce synchronization while creating objects. So, instead of waiting for 100 milliseconds, we can wait for 5 milliseconds.

So here is an example,

package SingletonDesign;  
 
class Singleton {  
 private static Singleton obj;  
 
 private Singleton() {  
 
    }  
 
 public static synchronized Singleton getInstance() //simple check 
    {  
 if (obj == null) {  
 synchronized(Singleton.class//double check loading 
            {  
 if (obj == null)  
                    obj = new Singleton();  
            }  
        }  
 return obj;  
    }  
}  
 
public class TheTech {  
 public static void main(String[] args) {  
        Thread t1 = new Thread(new Runnable() {  
 public void run() {  
                Singleton c1 = Singleton.getInstance();  
            }  
        });  
 
        Thread t2 = new Thread(new Runnable() {  
 public void run() {  
                Singleton c1 = Singleton.getInstance();  
            }  
        });  
 
        t1.start();  
        t2.start();  
    }  
}  

Enum with Singleton Pattern

To overcome the drawbacks of all four above implementations we have a new technique to create a singleton pattern.

From Java 1.5 we have one method to create a singleton design pattern and that method is thread-safe and utilizes fewer resources. This method will only work when we use the Java version above or 1.5.

Syntax

enum XYZ {
      INSTANCE; //inbuilt private constructor
}
 

Here's an example of using an enum (a special type of class).

package SingletonDesign;  
 
enum Singleton {  
    INSTANCE; //inbuilt private constructor 
 int x;  
 public void show() {  
        System.out.println(x);  
    }  
}  
 
public class TheTech {  
 public static void main(String[] args) {  
        Singleton object1 = Singleton.INSTANCE;  
        object1.x = 10;  
        object1.show();  
 
        Singleton object2 = Singleton.INSTANCE;  
        object2.x = 20;  
        object1.show();  
    }  
}  

The above code will result in output as 10 and 20 which shows singleton object implementation.

10
20

Even though we are working with Double-Check Locking(DCL) we have a concept of deserialization. So, even if the class is a singleton, the readObject() method will give you a new object.

Conclusion

The Singleton Design Pattern is a useful design pattern in Java that can help to ensure that only one instance of a class exists throughout the lifetime of the application. This pattern can help to manage resources efficiently, ensure a consistent state, and provide controlled access to objects. In this article, we discussed the different approaches to implementing the Singleton Design Pattern in Java, including eager initialization, lazy initialization, and thread-safe lazy initialization.

0 comments

Comments


bottom of page