Site icon JavaGoal

Singleton design pattern

Singleton pattern is part of the design pattern in java that is categorized in creational design patterns. In this post, we will read about singleton design pattern and what is the use of singleton class in java. The implementation of the Java Singleton pattern seems easy, but it is one of the most difficult topics in java. Let’s learn it in detail with some of the best practices for its usage.

Here is the table content of the article will we will cover this topic.
1. Singleton design pattern?
2. What is the use of singleton class?
3. How to create singleton class in java or how to make singleton class in java?
4. Eager initialization?
5. Static block initialization?
6. Lazy Initialization?
7. Thread Safe Singleton?
8. Bill Pugh Singleton Implementation?
9. Enum Singleton?
10. Serialization and Singleton?

Singleton design pattern

The Singleton design pattern ensures that there is only one object of a class is created in the Java Virtual Machine and it restricts a class to instantiate its multiple objects. It is just a way to define a class. It is used in complex projects so that a class can have only one object in the complete execution of the project. A Singleton class provides a global point of access to it. Singleton patterns are used in logging, caches, thread pools, configuration settings, device driver objects.

What is the use of singleton class?

We have read about the singleton class, but one question comes to mind. Why singleton class is used?
So the answer is here, suppose you are working on a big project and using a complex object with data. There are many points when you need to get and put data in an object. There may be a chance, that if you (Or a new developer) get an object of the class by mistake, then it will return a new object with blank data. But in the case of the singleton class, it retrieves a singleton object, that is common and global from each point.

As you can see in the image, A non-singleton class can have a different object in different ways. But a singleton class provides only one object in a single way. You can get objects in any other way. 

How to create a singleton class in java or how to make a singleton class in java

To create a singleton class, we have to follow some rules that must be implemented in your class.

1. Private constructor: We have to make our default constructor a private constructor in class because it will not accessible from outside the class. We have to prevent the outer classes or subclasses to create an object.

2. Static variable: We have to create a private static variable of the same class that is the only instance of the class.

3. Global access: We have to create a public static method that returns the object of the class and this method should be globally accessible so that each class can use it.

In the next section, we will learn different approaches to Singleton pattern implementation and design concerns with the implementation.

It is the easiest way to create a singleton class. In eager initialization, the object is created at the time of class loading. But it has a drawback that an object is always created in memory whether it is being used or not. It is also known as the early initialization of a singleton object.

1. Eager initialization

class Student
{
	// Make a static data member of instance
	private static final Student instance = new Student();
    
    //private constructor to avoid client applications to use constructor
    private Student()
    {}

    // A public static method that returns instance 
    public static Student getInstance()
    {
        return instance;
    }
}

public class StudentRecord
{
	public static void main(String arg[])
	{
		// Getting a object from singleton class
		Student stu1 = Student.getInstance();
		
		// Getting a object from singleton class
		Student stu2 = Student.getInstance();
		
		System.out.println("Both object are same: "+ (stu1 == stu2));
	}
}

Output: Both object are same: true

In the above example, we are creating a singleton class Student. The object of Student(singleton class) is automatically created during class loading. We can get the object by using of getInstance() method.

Disadvantage:

1. This type of singleton has a drawback if the singleton class is using a lot of resources. Because it loads all the resources during class loading without knowing whether they will be used or not.  In this case, we should avoid the creation of an object until unless client calls the getInstance method.

2. It doesn’t provide any exception handling code. We can’t get the reason for the exception if any exception occurs.

2. Static block initialization

The implementation of static block initialization is very similar to eager initialization. It initializes the object in the static block and also handles the exception at creation time. But still, it is not a best practice because objects are always created during class loading.

class Student
{
	// Make a static data member of instance
	private static Student instance;
    
    //private constructor to avoid client applications to use constructor
    private Student()
    {}
    static
    {
        try
        {
            instance = new Student();
        }catch(Exception e)
        {
            throw new RuntimeException("Exception occured in creating singleton instance");
        }
    }
    
    // A public static method that returns instance 
    public static Student getInstance()
    {
        return instance;
    }
}

public class StudentRecord
{
	public static void main(String arg[])
	{
		// Getting a object from singleton class
		Student stu1 = Student.getInstance();
		
		// Getting a object from singleton class
		Student stu2 = Student.getInstance();
		
		System.out.println("Both object are same: "+ (stu1 == stu2));
	}
}

Output: Both object are same: true

3. Lazy Initialization

In eager initialization and static initialization, the object was created during class loading but in Lazy initialization, it restricts the creation of the instance until it is requested for the first time. Here the object of the singleton class is created in the getInstance() method only if the object is not already created.

Disadvantage: The Lazy initialization works fine with a single thread environment but in a multithreading environment, it can create a problem. Suppose multiple threads try to get the object from the singleton class at the same time. If multiple threads enter the if condition at the same time, then it will destroy the singleton pattern. Because both threads will get different objects.

class Student
{
	// Make a static data member of instance
	private static Student instance;
    
    //private constructor to avoid client applications to use constructor
    private Student()
    {}
        
    // A public static method that returns instance 
    public static Student getInstance()
    { 
    	if(instance == null)
    	{
    		instance = new Student();
    	}
        return instance;
    }
}

public class StudentRecord
{
	public static void main(String arg[])
	{
		// Getting a object from singleton class
		Student stu1 = Student.getInstance();
		
		// Getting a object from singleton class
		Student stu2 = Student.getInstance();
		
		System.out.println("Both object are same: "+ (stu1 == stu2));
	}
}

Output: Both object are same: true

Above you can see the getInstance() method checks whether the instance is already created or not. If the instance is null, then it will create a new instance otherwise returns the existing object.

4. Thread Safe Singleton

As we have read Lazy Initialization is not thread-safe. Here we will discuss the way to create a thread-safe singleton class. We have to use the synchronized keyword with the global access method. So that only one can enter at a time. The general implementation of this approach is like the below class.

class Student
{
	// Make a static data member of instance
	private static Student instance;
    
    //private constructor to avoid client applications to use constructor
    private Student()
    {}
        
    // A public static method that returns instance 
    public static synchronized Student getInstance()
    { 
    	if(instance == null)
    	{
    		instance = new Student();
    	}
        return instance;
    }
}

public class StudentRecord
{
	public static void main(String arg[])
	{
		// Getting a object from singleton class
		Student stu1 = Student.getInstance();
		
		// Getting a object from singleton class
		Student stu2 = Student.getInstance();
		
		System.out.println("Both object are same: "+ (stu1 == stu2));
	}
}

Output: Both object are same: true

Disadvantage: It impacts the performance because the synchronized method can access by one thread at a time meanwhile other threads have to wait. Although we know we need it only for the first few threads that might create separate instances.

We can avoid such types of the situation by use of double-checked locking. In this approach, we use a synchronized block instead of a synchronized method.  We use the synchronization block inside the if statement with an additional check to ensure that only one instance of a singleton class is created.

class Student
{
	// Make a static data member of instance
	private static Student instance;
    
    //private constructor to avoid client applications to use constructor
    private Student()
    {}
        
    // A public static method that returns instance 
    public static Student getInstance()
    { 
    	if(instance == null)
    	{
    		synchronized(Student.class)
    		{
    			if(instance == null)
    				instance = new Student();
    		}
    	}
        return instance;
    }
}

public class StudentRecord
{
	public static void main(String arg[])
	{
		// Getting a object from singleton class
		Student stu1 = Student.getInstance();
		
		// Getting a object from singleton class
		Student stu2 = Student.getInstance();
		
		System.out.println("Both object are same: "+ (stu1 == stu2));
	}
}

Output: Both object are same: true

5. Bill Pugh Singleton Implementation

Prior to Java 5, the java memory model had a lot of issues in the above implementations of a singleton. These implementations can be failed in a multithreading environment when too many threads try to get the object simultaneously. Bill Pugh uses the static inner class to create an instance of the Singleton class.  This implementation creates an object only when we need an instance.

class Student
{
    //private constructor to avoid client applications to use constructor
    private Student()
    {}
    
    private static class SingletonHelper
    {
    	private static final Student INSTANCE = new Student();
    }
        
    // A public static method that returns instance 
    public static Student getInstance()
    { 
    	return SingletonHelper.INSTANCE;
    }
}

public class StudentRecord
{
	public static void main(String arg[])
	{
		// Getting a object from singleton class
		Student stu1 = Student.getInstance();
		
		// Getting a object from singleton class
		Student stu2 = Student.getInstance();
		
		System.out.println("Both object are same: "+ (stu1 == stu2));
	}
}

Output: Both object are same: true

In the above example, you can see we are using a private inner static class that is SingletonHelper that contains the instance of the Student(singleton class). When the JVM will load the Student class(Singleton class) then it will not load the  SingeltonHelper class into memory.  The SingeltonHelper class will load only when someone calls the getInstance() method.

6. Enum Singleton

If you are familiar with Reflection in java then you think the reflection can be used to destroy all the above singleton implementation approaches.

To resolve the above problem, we can use the Enum to implement the Singleton design pattern. As we know enum is used to hold the constants and an enum value is instantiated only once in a Java program. The members of the enum are static and final so they are globally accessible.

Drawback: It is like eager initialization because it will create instances whether it will be used or not and it does not allow lazy initialization.

enum Student
{
	INSTANCE;
	 public static void doSomething()
	 {
	     //do something
	 }
}

public class StudentRecord
{
	public static void main(String arg[])
	{
		// Getting a object from singleton enum
		Student stu1 = Student.INSTANCE;
		
		// Getting a object from singleton enum
		Student stu2 = Student.INSTANCE;
		
		System.out.println("Both object are same: "+ (stu1 == stu2));
	}
}

Output: Both object are same: true

7. Serialization and Singleton

Suppose we have a project that works based on distributed systems and we need to implement a Serializable interface in the Singleton class. We want to store its state in the file system and retrieve it at a later point in time.

import java.io.Serializable;

class Student implements Serializable
{
	private static final long serialVersionUID = -7604766932017737115L;

    private Student(){}
    
    private static class SingletonHelper{
        private static final Student instance = new Student();
    }
    
    public static Student getInstance(){
        return SingletonHelper.instance;
    }
}

public class StudentRecord
{
	public static void main(String arg[])
	{
		// Getting a object from singleton class
		Student stu1 = Student.getInstance();
		
		// Getting a object from singleton class
		Student stu2 = Student.getInstance();
		
		System.out.println("Both object are same: "+ (stu1 == stu2));
	}
}

Output: Both object are same: true

Exit mobile version