Threadsafe Singleton Design Pattern Java

Upasana | December 26, 2019 | 3 min read | 157 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. Morgan Stanley Java Interview Questions
  2. Citibank Java developer interview questions
  3. RBS Java Programming Interview Questions
  4. Multi-threading Java Interview Questions for Investment Bank
  5. UBS Java Interview Questions
  6. BlackRock Java Interview Questions
  7. ION Trading Java Interview Questions

Recommended books for interview preparation:

Find more on this topic: