Singleton Pattern with Lazy Instantiation and Double-Checked Locking

Intent: The singleton pattern is one of the creational patterns. It ensures that you create an object in memory only once in an application and provide a global point of access to it.

Motivation: There are times when you need for some classes to have exactly one instance. Let’s say that you are an author and have an online bookstore for your one title and you want to manage the amount of books available for your online customers accessing the store at the same time.

How do you ensure that your storage has only one instance and that the instance is easily accessible?

Solution: The singleton pattern gives the class the possibility to be responsible for keeping track of its sole instance. The one instance in memory is sharable by all the classes and threads within the application. Singletons can also improve performance by loading data that would otherwise be time consuming.

Implementation: This is an implementation of the BooksStorage class as a singleton:

public class BooksStorage {
 
    private int quantity = 0;
    private BooksStorage(){}
 
    private static final BooksStorage instance = new BooksStorage() ;
 
    public static BooksStorage getInstance(){
        return instance;
    }
    public synchronized void addBooks(int amount){
        quantity += amount;
    }
    public synchronized boolean removeBooks(int amount){
        if(quantity < amount) return false;
        quantity -= amount;
        return true;
    }
    public synchronized int getBooksQuantity(){
        return quantity;
    }
}

the public static method getInstance() is a single point of access which returns a reference to the singleton object. All constructors in a singleton class are marked private insuring that no other class is capable of instantiating another version of the class.

In our example, we instantiated the singleton object directly in the definition of the instance reference. There are other ways to instantiate a singleton object. One way is using a static initialization block:

    private static final BooksStorage instance ;
    static{
        instance = new BooksStorage();
        // other steps
    }

The static initialization block allows additional steps to be taken tom set up the singleton after it has been created.

Another way is to delay the creation of the singleton until the first time the getInstance() method is called. Creating a reusable object the first time it is requested is a pattern called lazy instantiation. We achieve it like this:

    private static BooksStorage instance ;
    public static synchronized BooksStorage getInstance(){
        if(instance == null){
            instance = new BooksStorage();
        }
        return instance;
    }

Lazy instantiation reduces memory usage when it does not create the singleton object at the class loading but rather the first time the object is requested by a client.

The synchronized implementation of getInstance() has the problem that every single call to the method will require synchronization. This can impact the performance and synchronization is only needed the first time that the object is created.

The solution is to use double-checked locking, a pattern in which we first test if synchronization is needed:

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

The volatile modifier is used to prevent the case where the compiler tries to optimize the code such that the object is accessed before it is finished being constructed.

Leave Comment

Your email address will not be published. Required fields are marked *