Singleton Design Pattern | Java

Anjali Mishra
3 min readJul 27, 2024

--

The Singleton Design Pattern is a creational design pattern that ensures a class has only one instance and provides a global point of access to that instance. This pattern is useful when you need exactly one instance of a class to control access to a shared resource.

Key Characteristics of Singleton Pattern

  1. Single Instance: Guarantees that only one instance of the class is created.
  2. Global Access Point: Provides a global access point to the instance.
  3. Controlled Access: Controls the access to the instance, often providing methods to manage its creation and lifecycle.

Common Uses of Singleton Pattern

  1. Database Connection: Managing a single database connection instance to avoid multiple connections and ensure efficient resource use.
  2. Configuration Management: Managing configuration settings for an application from a single source.
  3. Logging: Centralizing logging to ensure all log entries are managed by a single instance.

Here are explanations and Java implementations for a simple singleton, a thread-safe singleton, and a singleton that prevents cloning.

  • Simple Singleton: Ensures a single instance with a private constructor and a public static method.
  • Thread-Safe Singleton: Uses synchronized methods or double-checked locking to handle concurrent access.
  • Non-Cloneable Singleton: Prevents cloning by overriding the clone method to throw an exception.

1. Simple Singleton Implementation

A simple implementation of the Singleton pattern involves a private static instance of the class and a public static method to provide access to this instance.

public class Singleton {

// Private static variable of the single instance
private static Singleton instance;

// Private constructor to prevent instantiation
private Singleton() {}

// Public static method to provide access to the instance
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}

// Method for demonstration
public void showMessage() {
System.out.println("Hello from Singleton!");
}

public static void main(String[] args) {
Singleton singleton = Singleton.getInstance();
singleton.showMessage();
}
}

Explanation:

  • Private Constructor: Ensures no other class can instantiate the Singleton class.
  • Static Instance: Holds the single instance of the class.
  • Public Static Method: Provides a global point of access to the instance.

2. Thread-Safe Singleton Implementation

To make the singleton thread-safe, you can use synchronized methods or blocks.

Synchronized Method:

public class ThreadSafeSingleton {

private static ThreadSafeSingleton instance;

private ThreadSafeSingleton() {}

// Synchronized method to control simultaneous access
public static synchronized ThreadSafeSingleton getInstance() {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
return instance;
}

public void showMessage() {
System.out.println("Hello from ThreadSafeSingleton!");
}

public static void main(String[] args) {
ThreadSafeSingleton singleton = ThreadSafeSingleton.getInstance();
singleton.showMessage();
}
}

Double-Checked Locking:

public class ThreadSafeSingleton {

private static volatile ThreadSafeSingleton instance;

private ThreadSafeSingleton() {}

public static ThreadSafeSingleton getInstance() {
if (instance == null) {
synchronized (ThreadSafeSingleton.class) {
if (instance == null) {
instance = new ThreadSafeSingleton();
}
}
}
return instance;
}

public void showMessage() {
System.out.println("Hello from ThreadSafeSingleton!");
}

public static void main(String[] args) {
ThreadSafeSingleton singleton = ThreadSafeSingleton.getInstance();
singleton.showMessage();
}
}

Explanation:

  • Synchronized Method: Ensures that only one thread can execute the getInstance method at a time.
  • Double-Checked Locking: Minimizes the performance cost of synchronization by first checking if the instance is null without locking.

3. Singleton that Prevents Cloning

To prevent a singleton instance from being cloned, implement the Cloneable interface and override the clone method to throw a CloneNotSupportedException.

public class NonCloneableSingleton implements Cloneable {

private static NonCloneableSingleton instance;

private NonCloneableSingleton() {}

public static NonCloneableSingleton getInstance() {
if (instance == null) {
instance = new NonCloneableSingleton();
}
return instance;
}

// Override clone method to prevent cloning
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Singleton instance cannot be cloned");
}

public void showMessage() {
System.out.println("Hello from NonCloneableSingleton!");
}

public static void main(String[] args) {
NonCloneableSingleton singleton = NonCloneableSingleton.getInstance();
singleton.showMessage();
try {
NonCloneableSingleton clonedSingleton = (NonCloneableSingleton) singleton.clone();
} catch (CloneNotSupportedException e) {
System.out.println(e.getMessage());
}
}
}

Explanation:

  • Override clone Method: Throws a CloneNotSupportedException to ensure the singleton instance cannot be cloned.

Disadvantages of Singleton Pattern

  1. Global State: Introduces a global state into the application, which can lead to issues with testing and maintainability.
  2. Concurrency Issues: May cause concurrency issues if not properly synchronized.
  3. Hidden Dependencies: Can hide dependencies, making the code less transparent and harder to understand.

Summary

The Singleton Design Pattern is useful when you need a single, globally accessible instance of a class. It is commonly used for managing configuration settings, logging, and database connections. However, it should be used with care due to potential drawbacks like global state and concurrency issues.

--

--

Anjali Mishra

Experienced in system design, microservices, and backend development. Proficient in Spring Boot and NodeJS.