Singleton Design Pattern and how to make it thread-safe

Carvia Tech | May 23, 2019 | 3 min read | 0 views


Singleton is mostly considered an anti-Pattern, because it brings inherent complexity to the system in terms of testing. Only DI frameworks (Spring, Dagger, etc) should be allowed to create Singletons for you rather than you writing the singletons. However, if it is absolute essential to write your own Singleton, then you must write it correctly.

There are multiple ways you can create singletons in Java, depending upon the requirements - Lazy vs Eager Initialization, thread-safety, you can construct appropriate singleton in Java.

Single Thread Version (non thread-safe)

/src/main/java/Foo.java
// Single-threaded version
class Foo {
    private Helper helper;

    public Helper getHelper() {
        if (helper == null) {
            helper = new Helper();
        }
        return helper;
    }

    // other functions and members...
}

Thread-safe version

The problem with single threaded version is that it will not work predictably when multiple threads tries to access singleton. A lock must be obtained in case two or more threads call getHelper() simultaneously, otherwise there may be problems:

  1. Both threads may try to create the object at the same time, leading to two objects.

  2. one thread may end up getting a reference to an incompletely initialized object.

In this version of singleton, we obtain a lock by expensive method level synchronization technique, as illustrated in the below example:

/src/main/java/Foo.java
// Correct but possibly expensive multithreaded version
class Foo {
    private Helper helper;
    public synchronized Helper getHelper() {
        if (helper == null) {
            helper = new Helper();
        }
        return helper;
    }

    // other functions and members...
}

This approach is thread-safe and behavior of the Singleton is predictable in multi-threaded environment, but there are performance issues with this approach:

  1. synchronizing a method could in some extreme cases decrease the performance by a factor of 100 or higher.

  2. once the singleton object is initialized completely, acquiring and releasing the locks would appear unnecessary.

Optimization Algorithm:
  1. Check that the variable is initialized without obtaining the lock. If it is initialized, return it immediately.

  2. Obtain the lock

  3. Double-check whether the variable has already been initialized, if the another thread acquired the lock first, it may have already done the initialization. If so, return the initialized variable.

  4. Otherwise, initialize and return the variable.

Optimized version of thread-safe Singleton version using locking:

/src/main/java/Foo.java Optimized version
class Foo {
    private volatile Helper helper;

    public Helper getHelper() {
        Helper localRef = helper;
        if (localRef == null) {     (1)
            synchronized (this) {
                localRef = helper;  (2)
                if (localRef == null) {
                    helper = localRef = new Helper();
                }
            }
        }
        return localRef;    (3)
    }
    // other functions and members...
}
1 Using localRef, we are reducing the access of volatile variable to just one for positive usecase. If we do not use localRef, then we would have to access volatile variable twice - once for checking null and then at method return time. Accessing volatile memory is quite an expensive affair because it involves reaching out to main memory.
2 Refreshing local reference to latest value after acquiring a lock, since volatile variable may have changed by this time due.
3 volatile variable is accessed at method return time.

The volatile keyword ensures that multiple threads handle the singleton instance correctly.

Static Inner Class Implementation

If helper object is static i.e. only one instance per class loader, the best option is to use initialization-on-demand holder idiom.

Static Inner Class singleton relies on the fact that nested classes are not loaded until they are referenced. The following example explains it better:

/src/main/java/SingletonOne.java
public class SingletonOne {

    private SingletonOne() {
    }

    // Inner class to provide instance of class, inner static classes are lazily loaded by JVM, upon first invocation
    private static class HelperHolder {
        private static final SingletonOne instance = new SingletonOne();
    }

    public static SingletonOne getInstance() {
        return HelperHolder.instance;
    }
}

References


Buy my ebook for complete question bank

Most of these questions has been answered in my eBook "Cracking the Core Java Interview" updated on June 2018, that you can buy from this link:

Buy from Shunya (DRM Free PDF download with updates)

Top articles in this category:
  1. Top 50 Multi-threading Java Interview Questions for Investment Bank
  2. UBS Top 10 Java Interview Questions
  3. Citibank Java developer interview questions
  4. Morgan Stanley Java Interview Questions
  5. BlackRock Top Java Interview Questions: Investment Banking Domain
  6. Cracking core java interviews - question bank
  7. Top 20 Java Concurrency Interview Questions and Answers



Find more on this topic:
Java Interviews image
Java Interviews

Interview - Product Companies, eCommerce Companies, Investment Banking, Healthcare Industry, Service Companies and Startups.

Last updated 1 month ago


Recommended books for interview preparation:

This website uses cookies to ensure you get the best experience on our website. more info