Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we’ll discuss the Bill Pugh Singleton implementation. There are several implementations of the Singleton Pattern. Notably, Lazy Loaded Singleton and Eager Loaded Singleton implementations are the prominent ones. Furthermore, these also support synchronized and non-synchronized versions as well.

The Bill Pugh Singleton implementation supports lazy-loaded singleton object. In the upcoming sections, we’ll explore its implementation and see how it resolves the challenges faced by other implementations.

2. Basic Principles of Singleton Implementation

Singleton is a creational design pattern. As the name suggests, this design pattern helps create a single instance of a class. This instance is used throughout the application. It’s often used for classes that are expensive and time-consuming to create, such as Connection Factories, REST Adapters, Dao, etc. classes.

Before we move on, let’s first look at the basic principles of singleton implementation of a Java class:

  • Private constructors to prevent instantiation with the new operator
  • A public static method preferably with the name getInstance() to return a single instance of the class
  • A private static variable to store the only instance of the class

Furthermore, there could be challenges restricting a single instance of a class in a multi-threaded environment and deferring the initialization of the instance till it’s referred. Hence that’s where one implementation scores better than the others. Keeping these challenges in mind, we’ll see how the Bill Pugh Singleton implementation emerges as the winner.

3. Bill Pugh Singleton Implementation

Mostly, Singleton implementations face one or both of the following challenges:

  • Eager loading
  • Overhead due to synchronization

The Bill Pugh or Holder Singleton pattern addresses both of them with the help of a private static inner class:

public class BillPughSingleton {
    private BillPughSingleton() {

    }

    private static class SingletonHelper {
        private static final BillPughSingleton BILL_PUGH_SINGLETON_INSTANCE = new BillPughSingleton();
    }

    public static BillPughSingleton getInstance() {
        return SingletonHelper.BILL_PUGH_SINGLETON_INSTANCE;
    }
}

The class loader in a Java application loads the static inner class SingletonHelper in the memory only once, even if multiple threads call getInstance(). Noticeably, we’re not using synchronized as well. This eliminates the overhead of locking and unlocking objects while accessing the synchronized methods. So this approach addresses the flaws faced by other implementations.

Now, let’s see how it works:

@Test
void givenSynchronizedLazyLoadedImpl_whenCallgetInstance_thenReturnSingleton() {
    Set<BillPughSingleton> setHoldingSingletonObj = new HashSet<>();
    List<Future<BillPughSingleton>> futures = new ArrayList<>();

    ExecutorService executorService = Executors.newFixedThreadPool(10);
    Callable<BillPughSingleton> runnableTask = () -> {
        logger.info("run called for:" + Thread.currentThread().getName());
        return BillPughSingleton.getInstance();
    };

    int count = 0;
    while(count < 10) {
        futures.add(executorService.submit(runnableTask));
        count++;
    }
    futures.forEach(e -> {
        try {
            setHoldingSingletonObj.add(e.get());
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    });
    executorService.shutdown();
    assertEquals(1, setHoldingSingletonObj.size());
}

In the above method, multiple threads call getInstance() concurrently. However, it always returns the same object reference.

4.  Bill Pugh vs Unsynchronized Implementations

Let’s implement the Singleton Pattern for both single-threaded and multi-threaded environments. We’ll avoid using the synchronized keyword for the implementation in the multi-threaded environment.

4.1. Lazy-Loaded Singleton Implementation

Based upon the basic principles described earlier, let’s implement a singleton class:

public class LazyLoadedSingleton {
    private static LazyLoadedSingleton lazyLoadedSingletonObj;

    private LazyLoadedSingleton() {
    }

    public static LazyLoadedSingleton getInstance() {
        if (null == lazyLoadedSingletonObj) {
            lazyLoadedSingletonObj = new LazyLoadedSingleton();
        }
        return lazyLoadedSingletonObj;
    }
}

LazyLoadedSingleton object gets created only when the getInstance() method is called. This is called lazy initialization. However, this fails as a result of a dirty read when multiple threads concurrently call the getInstance() method. This isn’t the case with Bill Pugh’s implementation even without the use of synchronized.

Let’s see if the class LazyLoadedSingleton creates only a single object:

@Test
void givenLazyLoadedImpl_whenCallGetInstance_thenReturnSingleInstance() throws ClassNotFoundException {

    Class bs = Class.forName("com.baledung.billpugh.LazyLoadedSingleton");
    assertThrows(IllegalAccessException.class, () -> bs.getDeclaredConstructor().newInstance());

    LazyLoadedSingleton lazyLoadedSingletonObj1 = LazyLoadedSingleton.getInstance();
    LazyLoadedSingleton lazyLoadedSingletonObj2 = LazyLoadedSingleton.getInstance();
    assertEquals(lazyLoadedSingletonObj1.hashCode(), lazyLoadedSingletonObj2.hashCode());
}

The above method tries to instantiate LazyLoadedSingleton with the help of reflection API and by calling getInstance() method. However, instantiation fails with reflection, and getInstance() always returns a single instance of the class LazyLoadedSingleton.

4.2. Eager-Loaded Singleton Implementation

The implementation discussed in the earlier section works only in a single-threaded environment. However, for a multi-threaded environment, we can consider a different approach using a class-level static variable:

public class EagerLoadedSingleton {
    private static final EagerLoadedSingleton EAGER_LOADED_SINGLETON = new EagerLoadedSingleton();

    private EagerLoadedSingleton() {
    }

    public static EagerLoadedSingleton getInstance() {
        return EAGER_LOADED_SINGLETON;
    }
}

The class level variable EAGER_LOADED_SINGLETON is static. Hence, when the application starts, it loads it immediately even when it’s not needed. However, as discussed earlier, Bill Pugh’s implementation supports lazy loading in both single and multi-threaded environments.

Let’s see the EagerLoadedSingleton class in action:

@Test
void givenEagerLoadedImpl_whenCallgetInstance_thenReturnSingleton() {
    Set<EagerLoadedSingleton> set = new HashSet<>();
    List<Future<EagerLoadedSingleton>> futures = new ArrayList<>();

    ExecutorService executorService = Executors.newFixedThreadPool(10);

    Callable<EagerLoadedSingleton> runnableTask = () -> {
            return EagerLoadedSingleton.getInstance();
    };

    int count = 0;
    while(count < 10) {
        futures.add(executorService.submit(runnableTask));
        count++;
    }

    futures.forEach(e -> {
        try {
            set.add(e.get());
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    });
    executorService.shutdown();

    assertEquals(1, set.size());
}

In the above method, multiple threads call runnableTask. Then, the run() method calls getInstance() to fetch an instance of EagerLoadedSingleton. However, every time getInstance() returns a single instance of the object.

The above works in a multi-threaded environment but it exhibits eager loading, which is clearly a drawback.

5. Bill Pugh vs Synchronized Singleton Implementation

Earlier, we saw LazyLoadedSingleton in a single-threaded environment. Let’s modify it to support singleton pattern in a multi-threaded environment:

public class SynchronizedLazyLoadedSingleton {
    private static SynchronizedLazyLoadedSingleton synchronizedLazyLoadedSingleton;

    private SynchronizedLazyLoadedSingleton() {
    }

    public static synchronized SynchronizedLazyLoadedSingleton getInstance() {
        if (null == synchronizedLazyLoadedSingleton) {
            synchronizedLazyLoadedSingleton = new SynchronizedLazyLoadedSingleton();
        }
        return synchronizedLazyLoadedSingleton;
    }
}

Interestingly, by using the synchronized keyword on the method getInstance(), we restrict threads from accessing it concurrently. We can achieve a more performant variant of this using the double-checked locking method.

However, Bill Pugh’s implementation is clearly a winner because it can be used in a multi-threaded environment without the overhead of synchronization.

Let’s confirm if this works in a multi-threaded environment:

@Test
void givenSynchronizedLazyLoadedImpl_whenCallgetInstance_thenReturnSingleton() {
    Set<SynchronizedLazyLoadedSingleton> setHoldingSingletonObj = new HashSet<>();
    List<Future<SynchronizedLazyLoadedSingleton>> futures = new ArrayList<>();

    ExecutorService executorService = Executors.newFixedThreadPool(10);
    Callable<SynchronizedLazyLoadedSingleton> runnableTask = () -> {
        logger.info("run called for:" + Thread.currentThread().getName());
        return SynchronizedLazyLoadedSingleton.getInstance();
    };

    int count = 0;
    while(count < 10) {
        futures.add(executorService.submit(runnableTask));
        count++;
    }
    futures.forEach(e -> {
        try {
            setHoldingSingletonObj.add(e.get());
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    });
    executorService.shutdown();
    assertEquals(1, setHoldingSingletonObj.size());
}

Just like EagerLoadedSingleton, the SynchronizedLazyLoadedSingleton class also returns a single object in a multi-threaded setup. But this time the program loads the singleton object in a lazy fashion. However, it comes with an overhead because of synchronization.

6. Conclusion

In this article, we compared the Bill Pugh Singleton Implementation with other prevalent singleton implementations. Bill Pugh Singleton’s implementation performs better and supports lazy loading. Hence, many applications and libraries use it widely.

As usual, the code used in this article can be found over on GitHub.

Course – LS – All

Get started with Spring and Spring Boot, through the Learn Spring course:

>> CHECK OUT THE COURSE
res – REST with Spring (eBook) (everywhere)
Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.