Skip to content

Singleton Design Pattern

Ramesh Fadatare edited this page Aug 23, 2018 · 1 revision

Overview

In this article we will discuss following points:

Table of Contents

  1. Intent
  2. Explanation
  3. Structure
  4. Participants
  5. Implementation

5.1 Thread Safe Singleton

5.2 Double check locking

5.3 Eager Initialization

5.4 Enum Singleton

  1. Applicability

6.1 Typical Use Case 7. Real world examples 8. Consequences

1. Intent

Ensure a class only has one instance, and provide a global point of access to it.

2. Explanation

Real world example

There can only be one ivory tower where the wizards study their magic. The same enchanted ivory tower is always used by the wizards. Ivory tower here is singleton.

In plain words

Ensures that only one object of a particular class is ever created.

Wikipedia says

In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to one object. This is useful when exactly one object is needed to coordinate actions across the system.

3. Structure

diagram here

4. Participants

Singleton

  • Defines an Instance operation that lets clients access its unique instance. Instance is a class operation

  • May be responsible for creating its own unique instance.

Tips to create Singleton design pattern

To create the singleton class, we need to have a static data member of class, private constructor, and static factory method.

  • Static member: It gets memory only once because of static, it contains the instance of the Singleton class.
  • Private constructor: It will prevent to instantiate the Singleton class from outside the class.
  • Static factory method: This provides the global point of access to the Singleton object and returns the instance to the caller.

5. Implementation

5.1 Thread Safe Singleton

Lazy initialization method to implement Singleton pattern creates the instance in the global access method. Here is the sample code for creating Singleton class with this. The instance is lazily initialized and thus needs synchronization mechanism.

package com.ramesh.singleton;

public final class ThreadSafeLazyLoadedIvoryTower {

    private static ThreadSafeLazyLoadedIvoryTower instance;

    private ThreadSafeLazyLoadedIvoryTower() {
        // to prevent instantiating by Reflection call
        if (instance != null) {
            throw new IllegalStateException("Already initialized.");
        }
    }

    /**
     * The instance gets created only when it is called for first time. Lazy-loading
     */
    public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() {

        if (instance == null) {
            instance = new ThreadSafeLazyLoadedIvoryTower();
        }

        return instance;
    }
}

Above implementation works fine and provides thread-safety but it reduces the performance because of cost associated with the synchronized method, although we need it only for the first few threads who might create the separate instances . To avoid this extra overhead every time, double checked locking principle is used. In this approach, the synchronized block is used inside if condition with an additional check to ensure that only one instance of singleton class is created.

5.2 Double check locking

public final class ThreadSafeDoubleCheckLocking {

    private static volatile ThreadSafeDoubleCheckLocking instance;

    /**
     * private constructor to prevent client from instantiating.
     */
    private ThreadSafeDoubleCheckLocking() {
        // to prevent instantiating by Reflection call
        if (instance != null) {
            throw new IllegalStateException("Already initialized.");
        }
    }

    /**
     * Public accessor.
     *
     * @return an instance of the class.
     */
    public static ThreadSafeDoubleCheckLocking getInstance() {
        // local variable increases performance by 25 percent
        // Joshua Bloch "Effective Java, Second Edition", p. 283-284

        ThreadSafeDoubleCheckLocking result = instance;
        // Check if singleton instance is initialized. If it is initialized then we can return the instance.
        if (result == null) {
            // It is not initialized but we cannot be sure because some other thread might have initialized it
            // in the meanwhile. So to make sure we need to lock on an object to get mutual exclusion.
            synchronized(ThreadSafeDoubleCheckLocking.class) {
                // Again assign the instance to local variable to check if it was initialized by some other thread
                // while current thread was blocked to enter the locked zone. If it was initialized then we can 
                // return the previously created instance just like the previous null check.
                result = instance;
                if (result == null) {
                    // The instance is still not initialized so we can safely (no other thread can enter this zone)
                    // create an instance and make it our singleton instance.
                    instance = result = new ThreadSafeDoubleCheckLocking();
                }
            }
        }
        return result;
    }
}

5.3 Eager Initialization

In eager initialization, the instance of Singleton Class is created at the time of class loading, this is the easiest method to create a singleton class but it has a drawback that instance is created even though client application might not be using it. Eagerly initialized static instance guarantees thread safety.

package com.ramesh.singleton;
public final class IvoryTower {

    /**
     * Private constructor so nobody can instantiate the class.
     */
    private IvoryTower() {}

    /**
     * Static to class instance of the class.
     */
    private static final IvoryTower INSTANCE = new IvoryTower();

    /**
     * To be called by user to obtain instance of the class.
     *
     * @return instance of the singleton.
     */
    public static IvoryTower getInstance() {
        return INSTANCE;
    }
}

The Initialize-on-demand-holder idiom is a secure way of creating a lazy initialized singleton object in Java.

package com.ramesh.singleton;

public final class InitializingOnDemandHolderIdiom {

    /**
     * Private constructor.
     */
    private InitializingOnDemandHolderIdiom() {}

    /**
     * @return Singleton instance
     */
    public static InitializingOnDemandHolderIdiom getInstance() {
        return HelperHolder.INSTANCE;
    }

    /**
     * Provides the lazy-loaded Singleton instance.
     */
    private static class HelperHolder {
        private static final InitializingOnDemandHolderIdiom INSTANCE =
            new InitializingOnDemandHolderIdiom();
    }
}

5.4 Enum Singleton

To overcome this situation with Reflection, Joshua Bloch suggests the use of Enum to implement Singleton design pattern as Java ensures that any enum value is instantiated only once in a Java program. Since Java Enum values are globally accessible, so is the singleton. The drawback is that the enum type is somewhat inflexible; for example, it does not allow lazy initialization.

package com.ramesh.singleton;

/**
 * Enum based singleton implementation. Effective Java 2nd Edition (Joshua Bloch) p. 18
 */
public enum EnumIvoryTower {

    INSTANCE;

    @Override
    public String toString() {
        return getDeclaringClass().getCanonicalName() + "@" + hashCode();
    }
}

5.5 Test singleton design pattern

public class App {

    private static final Logger LOGGER = LoggerFactory.getLogger(App.class);

    /**
     * Program entry point.
     *
     * @param args command line args
     */
    public static void main(String[] args) {

        // eagerly initialized singleton
        IvoryTower ivoryTower1 = IvoryTower.getInstance();
        IvoryTower ivoryTower2 = IvoryTower.getInstance();
        LOGGER.info("ivoryTower1={}", ivoryTower1);
        LOGGER.info("ivoryTower2={}", ivoryTower2);

        // lazily initialized singleton
        ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 =
            ThreadSafeLazyLoadedIvoryTower.getInstance();
        ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 =
            ThreadSafeLazyLoadedIvoryTower.getInstance();
        LOGGER.info("threadSafeIvoryTower1={}", threadSafeIvoryTower1);
        LOGGER.info("threadSafeIvoryTower2={}", threadSafeIvoryTower2);

        // enum singleton
        EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE;
        EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE;
        LOGGER.info("enumIvoryTower1={}", enumIvoryTower1);
        LOGGER.info("enumIvoryTower2={}", enumIvoryTower2);

        // double checked locking
        ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance();
        LOGGER.info(dcl1.toString());
        ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance();
        LOGGER.info(dcl2.toString());

        // initialize on demand holder idiom
        InitializingOnDemandHolderIdiom demandHolderIdiom =
            InitializingOnDemandHolderIdiom.getInstance();
        LOGGER.info(demandHolderIdiom.toString());
        InitializingOnDemandHolderIdiom demandHolderIdiom2 =
            InitializingOnDemandHolderIdiom.getInstance();
        LOGGER.info(demandHolderIdiom2.toString());
    }
}

6. Applicability

Use the Singleton pattern when

  • there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point.
  • when the sole instance should be extensible by subclassing, and clients should be able to use an extended instance without modifying their code.

6.1 Typical Use Case

Singleton pattern is used for logging, driver objects, caching and thread pool. Singleton design pattern is also used in other design patterns like Abstract Factory, Builder, Prototype, Facade etc.

7. Real world examples

java.awt.Desktop#getDesktop()

java.lang.System#getSecurityManager()

java.lang.Runtime#getRuntime()

8. Consequences

Violates Single Responsibility Principle (SRP) by controlling their own creation and lifecycle.

  • Encourages using a global shared instance which prevents an object and resources used by this object from being deallocated.
  • Creates tightly coupled code. The clients of the Singleton become difficult to test.
  • Makes it almost impossible to subclass a Singleton.
Clone this wiki locally