Singleton Design Pattern

Implementing the Singleton Design Pattern

How will you ensure that a class has only one instance? There are several ways of doing it in Java.ย  Following are the ways:

  1. Declare a private constructor to prevent others from making object of class.
  2. Create the object of the class either during class loading in a staticย  block, or in a static method that first checks whether the object exists or not and creates a new object only if it doesnโ€™t exist.

Letโ€™s see all the possible solutions with code samples one by one:

1. Eagerly Initialized Singleton

This is the simplest approach in which object is created at the time of class loading –

public class EagerSingleton {

    /** private constructor to prevent others from instantiating this class */
    private EagerSingleton() {}

    /** Create an instance of the class at the time of class loading */
    private static final EagerSingleton instance = new EagerSingleton();

    /** Provide a global point of access to the instance */
    public static EagerSingleton getInstance() {
        return instance;
    }
}

The disadvantage of this approach is that the instance is created irrespective of whether it is used or not. It can have performance implication as object will be created without its use.

2. Eagerly Initialized Static Block Singleton

We can also create the one instance of the class in a static block. It works because the static block is executed only once at the time of class loading.

The benefit with static block initialization is that you can write your initialization logic or handle exceptions in the static block.

public class EagerStaticBlockSingleton {

    private static final EagerStaticBlockSingleton instance;

    /** Don't let anyone else instantiate this class */
    private EagerStaticBlockSingleton() {}

    /** Create the one-and-only instance in a static block */
    static {
        try {
            instance = new EagerStaticBlockSingleton();
        } catch (Exception ex) {
            throw ex;
        }
    }

    /** Provide a public method to get the instance that we created */
    public static EagerStaticBlockSingleton getInstance() {
        return instance;
    }
}

Like the previous solution, the instance is created whether or not it is needed by the application.

3. Lazily Initialized Singleton

Lazy initialization means making object only when it is needed.

In the following example, we first check whether the object is already created or not in theย getInstance()ย method. If the object is already created, we simply return it, otherwise, we first create the object and then return it:

public class LazySingleton {

    private static LazySingleton instance;

    /** Don't let anyone else instantiate this class */
    private LazySingleton() {}

    /** Lazily create the instance when it is accessed for the first time */
    public static synchronized LazySingleton getInstance() {
        if(instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

Focus on the use ofย synchronizedย keyword in theย getInstance()ย method. This is required to prevent race conditions in multi-threaded environments.

Letโ€™s say that theย objectย is not created yet, and two threads enter theย getInstance()ย method simultaneously. In that case, theย instance==nullย check will be equal to true and both the threads will create a new object of the class.

Theย synchronizedย keyword ensures that only one thread can execute theย getInstance()ย method at one time.

4. Lazily Initialized Double-Checked Locking Singleton

Theย synchronizedย keyword in theย getInstance()ย method prevents race conditions, but it also has some performance issue

Below is an optimized way of the lazily initialized singleton whereย  instead of making the entire methodย synchronized, we create aย synchronizedย block and write only the instantiation part in theย synchronizedย block –

public class LazyDoubleCheckedLockingSingleton {

    private static volatile LazyDoubleCheckedLockingSingleton instance;

    /** private constructor to prevent others from instantiating this class */
    private LazyDoubleCheckedLockingSingleton() {}

    /** Lazily initialize the singleton in a synchronized block */
    public static LazyDoubleCheckedLockingSingleton getInstance() {
        if(instance == null) {
            synchronized (LazyDoubleCheckedLockingSingleton.class) {
                // double-check
                if(instance == null) {
                    instance = new LazyDoubleCheckedLockingSingleton();
                }
            }
        }
        return instance;
    }
}

The above approach is calledย Double-Checked Lockingย because we double-check whether the variable is initialized or not inside theย synchronizedย block.

The double-checking is very important here. Letโ€™s take that two threadsย T1ย andย T2ย enter theย getInstance()ย method at the same time. Theย instance==nullย check will be equal to true, so both of them will enter into theย synchronizedย block one-by-one. If the double check was not there, both threads would have created a new instance.

Also, focus on the use ofย volatileย keyword with the instance variable. This is important to prevent compilers from doing their own optimizations and handle the singleton correctly.

5. Lazily Initialized Inner Class Singleton (Bill Pugh singleton)

Bill Pugh came up with a highly efficient solution to create singleton. It is calledย Initialization-on-demand holder idiom. In this , a static inner class is used to lazily create a singleton object.

public class LazyInnerClassSingleton {

    /** private constructor to prevent others from instantiating this class */
    private LazyInnerClassSingleton() {}

    /** This inner class is loaded only after getInstance() is called for the first time. */
    private static class SingletonHelper {
        private static final LazyInnerClassSingleton INSTANCE = new LazyInnerClassSingleton();
    }

    public static LazyInnerClassSingleton getInstance() {
        return SingletonHelper.INSTANCE;
    }
}

Note that, the inner class is not loaded unless theย getInstance()ย method is invoked for the first time. This way of solution is thread-safe and doesnโ€™t require any synchronization. It isย  most efficient solution among all the singleton design pattern implementations.

6. Enum Singleton

An Enum is singleton by design. All the enum values are initialized only once during class loading.

import java.util.Arrays;

/** An Enum value is initialized only once at the time of class loading.
    It is singleton by design and is also thread-safe.
 */
enum EnumSingleton {
    WEEKDAY("Monday", "Tuesday", "Wednesday", "Thursday", "Friday"),
    WEEKEND("Saturday", "Sunday");

    private String[] days;

    EnumSingleton(String ...days) {
        System.out.println("Initializing enum with " + Arrays.toString(days));
        this.days = days;
    }

    public String[] getDays() {
        return this.days;
    }

    @Override
    public String toString() {
        return "EnumSingleton{" +
                "days=" + Arrays.toString(days) +
                '}';
    }
}

public class EnumSingletonExample {
    public static void main(String[] args) {
        System.out.println(EnumSingleton.WEEKDAY);
        System.out.println(EnumSingleton.WEEKEND);
    }
}
# Output
Initializing enum with [Monday, Tuesday, Wednesday, Thursday, Friday]
Initializing enum with [Saturday, Sunday]
EnumSingleton{days=[Monday, Tuesday, Wednesday, Thursday, Friday]}
EnumSingleton{days=[Saturday, Sunday]}

 

Newsletter Updates

Enter your name and email address below to subscribe to our newsletter

Leave a Reply