Ways to implement the Singleton pattern in Java

After you got started with Java programming, chances are you will want to use some design patterns to organise your Java classes.

One common object oriented design pattern is the Singleton pattern. The Singleton pattern is a technique where you can apply to ensure that there is only one instance of your Java class per runtime.

The Singleton pattern is usually used for grouping functionalities that allow callers to act on a single set of state. Examples of cases where the Singleton pattern can be applied include:

  • A class that keeps track of a customized configurations from a configuration file.
  • A class that facilitates interactions with a database system.
  • A class that performs logging.
  • A class that caches data read from previous database calls.

This post documents some ways to implement the Singleton pattern in Java.

How to make a class a Singleton in Java?

  • Declare every constructor private in the class to prevent instantiation from outside of the class.
  • Allocate memory to hold the single object instance of the class, making it accessible only to methods within the class.
  • Expose at least one static method for the retrieval of that single object instance from outside the class.

Lazy initialization of object in a single threaded environment

If the Java program that we are coding runs only in the main thread and there is a high chance that our Singleton object never get initialized, we may opt for the following implementation:

public class Singleton {
    private static Singleton single;

    private Singleton() {
        /*Some state initialization*/
    }    

    public static Singleton getInstance() {
        if (single == null) {
            single = new Singleton();
        }
        return single;
    }
}

In the above implementation, we first declare a static instance of Singleton right after the class definition on line 2. Since we do not assign any value to the single variable, the variable will be null after the Singleton class is loaded by the Java Classloader.

We then declare a private constructor to ensure that all constructors in the Singleton class are private on line 4. Keep in mind that even in cases where we do not have any statements for our constructor, we will need to define a private constructor as the default constructor will be implicitly declared as public due to the class being public.

Finally on line 8, we define a static method that returns an object of type Singleton. Inside the method definition, we first check if the single variable is null. If single varible is null, we proceed to use the private constructor to create an instance of the Singleton class.

Lazy initialization of object in a multithreaded environment

In a multithreaded environment, the previous implementation could result in 2 instances of Singleton being created by 2 different threads via the getInstance method.

This is because a first thread could invoke the getInstance method and gets preempted after determining that the single variable is null. A second thread could come along to invoke the getInstance method which cause the first instance of Singleton to be created. That first thread would then resume operation on line 10 which would cause the second instance of Singleton to be created.

To solve this issue, we can modify the first implementation of the Singleton pattern to resemble the following:

public class Singleton {
    private static Singleton single;

    private Singleton() {
        /*Some state initialization*/
    }    

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

Since the Singleton pattern could be broken from a thread being preempted after determining that the single variable is null, we introduce a synchronized block on the Singleton.class variable as the first statement in the first conditional block.

Since there could be a chance that a thread gets preempted before obtaining the lock on the single variable, we check the single variable again inside of the synchronized block.

This will ensure that only a single instance of the Singleton class can be created in a multithreaded environment.

Initialization of object right after the class is loaded by the Classloader

Creation of Singleton instance at the point where we declare it.

If our Singleton object will always be used for every program run, it will be easier to initialize the Singleton object right after the class is loaded by the Classloader:

public class Singleton {
    private static Singleton single = new Singleton();

    private Singleton() {
        /*Some state initialization*/
    }    

    public static Singleton getInstance() {
        return single;
    }
}

In the above implementation, we initialized the single variable at the point where it is declared.

Since we are sure that the single variable is always created after the class is loaded by the Classloader, we do not need to waste our CPU resources to check whether the single variable is null every time the getInstance method is invoked.

Creation of Singleton instance via the static initializer

We can also use the static initializer to make sure that the single variable is always created after the class is loaded by the Classloader:

public class Singleton {
    private static Singleton single;
    
    static {
        single = new Singleton();
    }
    
    private Singleton() {
        /*Some state initialization*/
    }    

    public static Singleton getInstance() {
        return single;
    }
}

In the above implementation, we initialized the Singleton instance in a block that is marked with the static keyword. This approach allows us to implement some retry mechanism in the event when we fail to initialize the Singleton object in our first attempt.

About Clivant

Clivant a.k.a Chai Heng enjoys composing software and building systems to serve people. He owns techcoil.com and hopes that whatever he had written and built so far had benefited people. All views expressed belongs to him and are not representative of the company that he works/worked for.