Skip to content

Singleton pattern

Singleton Pattern is a creational design pattern that guarantees a class has only one instance and provides a global point of access to it.

  • Global Access Point: Provides a single point of access to a shared resource
  • Lazy Initialization: Instance is created only when needed
  • Single Instance: Ensures only one instance exists throughout the application
  • Reduced Memory Footprint: Avoids creating multiple instances of resource-heavy objects

To implement the singleton pattern, we must prevent external objects from creating instances of the singleton class. Only the singleton class should be permitted to create its own objects.

Additionally, we need to provide a method for external objects to access the singleton object.

This can be achieved by making the constructor private and providing a static method for external objects to access it.

Diagram

Ways to implement the singleton pattern -

This approach creates the singleton instance only when it is needed, saving resources if the singleton is never used in the application.

public class Singleton {
private static Singleton instance;
private Singleton() {
// private constructor to prevent instantiation
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
  • Controlled access to the sole instance
  • Reduced memory footprint
  • Global point of access

Same as lazy initialization, but with added synchronization to ensure thread safety.

Diagram

Lazy Initialization | Thread-Safe Singleton

public class Singleton {
private static Singleton instance;
private Singleton() {
// private constructor to prevent instantiation
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
  • Controlled access to the sole instance
  • Thread-safe
  • Global point of access

* contention refers to the competition among multiple threads for access to shared resources. when one thread acquires a lock on a shared resource, and other threads that also need to access that same resource are forced to wait until the first thread releases the lock. This waiting period is a form of contention and can lead to performance bottlenecks, as threads are not able to execute concurrently as intended.

Diagram

Thread-Safe Singleton | Double-Checked Locking

This approach minimizes performance overhead from synchronization by only synchronizing when the object is first created.

It uses the volatile keyword to ensure that changes to the instance variable are immediately visible to other threads.

public class Singleton {
private static volatile Singleton instance;
private Singleton() {
// private constructor to prevent instantiation
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
  • Controlled access to the sole instance
  • Thread-safe
  • Reduced performance overhead compared to synchronized method

This approach creates the singleton instance at the time of class loading, ensuring that the instance is always available when needed.

In this method, we rely on the JVM to create the singleton instance when the class is loaded. The JVM guarantees that the instance will be created before any thread access the instance variable.

This implementation is one of the simplest and inherently thread-safe without needing explicit synchronization.

public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
// private constructor to prevent instantiation
}
public static Singleton getInstance() {
return instance;
}
}
  • Simple and easy to implement
  • Thread-safe without synchronization
  • Instance is created at class loading time, ensuring it’s always available

5. Bill Pugh Singleton or Lazy Holder Singleton

Section titled “5. Bill Pugh Singleton or Lazy Holder Singleton”
Diagram

Lazy Initialization | Eager Initialization

This is a variation of the lazy initialization pattern that uses a static inner helper class to hold the instance variable.

This implementation uses a static inner helper class to hold the singleton instance. The inner class is not loaded into memory until it’s referenced for the first time in the getInstance() method.

It is thread-safe without requiring explicit synchronization.

The Bill Pugh Singleton implementation, while more complex than Eager Initialization provides a perfect balance of lazy initialization, thread safety, and performance, without the complexities of some other patterns like double-checked locking.

public class BillPughSingleton {
private BillPughSingleton() {
// private constructor to prevent instantiation
}
private static class SingletonHelper {
private static final BillPughSingleton INSTANCE = new BillPughSingleton();
}
public static BillPughSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
  • Thread-safe without synchronization
  • Lazy initialization
  • No performance overhead from synchronization

In this method, the singleton is declared as an enum rather than a class.

Java ensures that only one instance of an enum value is created, even in a multithreaded environment.

The Enum Singleton pattern is the most robust and concise way to implement a singleton in Java.

public enum EnumSingleton {
INSTANCE;
// Add any necessary methods here
}
// Usage
public class Main {
public static void main(String[] args) {
EnumSingleton singleton = EnumSingleton.INSTANCE;
// Use the singleton instance
}
}
  • Simple and concise implementation
  • Thread-safe by design
  • No need for synchronization
Diagram

Eager Initialization

This is similar to eager initialization, but the instance is created in a static block.

It provides the ability to handle exceptions during instance creation, which is not possible with simple eager initialization.

public class StaticBlockSingleton {
private static StaticBlockSingleton instance;
static {
try {
instance = new StaticBlockSingleton();
} catch (Exception e) {
throw new RuntimeException("Exception occurred while creating singleton instance", e);
}
}
private StaticBlockSingleton() {
// private constructor to prevent instantiation
}
public static StaticBlockSingleton getInstance() {
return instance;
}
}
  • Simple and easy to implement
  • Thread-safe without synchronization
  • Allows exception handling during instance creation

This approach uses a ThreadLocal variable to ensure that each thread has its own instance of the singleton. This is useful in scenarios where each thread needs its own instance of the singleton, such as in web applications where each request is handled by a separate thread.

public class ThreadLocalSingleton {
private static ThreadLocal<ThreadLocalSingleton> instance = ThreadLocal.withInitial(ThreadLocalSingleton::new);
private ThreadLocalSingleton() {
// private constructor to prevent instantiation
}
public static ThreadLocalSingleton getInstance() {
return instance.get();
}
}
  • Each thread has its own instance, avoiding contention
  • Reduces memory footprint by not sharing instances between threads
  • Useful in multi-threaded applications where each thread needs its own instance

This approach allows for multiple singleton instances, each identified by a unique key. It provides a registry to manage these instances.

import java.util.HashMap;
import java.util.Map;
public class RegistrySingleton {
private static Map<String, RegistrySingleton> instances = new HashMap<>();
private RegistrySingleton() {
// private constructor to prevent instantiation
}
public static RegistrySingleton getInstance(String key) {
return instances.computeIfAbsent(key, k -> new RegistrySingleton());
}
}
  • Allows for multiple singleton instances identified by unique keys
  • Provides a centralized registry for managing instances
  • Can be useful in scenarios where different configurations are needed for different instances