Singleton Design Pattern in Java
Learn Singleton Design Pattern in Java with purpose, real-time examples, framework examples, diagrams, code, input/output, benefits, limitations, and interview questions.
Introduction
Singleton is one of the most commonly used Creational Design Patterns in Java.
It ensures that a class has only one object instance in the entire application and provides a global access point to that instance.
Purpose of Singleton Pattern
The main purpose of Singleton is:
Create only one object and reuse it everywhere.
Singleton is useful when one shared object is enough for the complete application.
Real-Time Analogy
Think about a company CEO.
A company may have many employees, departments, and managers.
But usually there is only one CEO.
Everyone refers to the same CEO.
Company → One CEO
Application → One Singleton Object
Singleton Pattern Diagram
flowchart TD
A[Application Code]
A --> B[getInstance Method]
B --> C{Instance Already Created?}
C -->|Yes| D[Return Existing Object]
C -->|No| E[Create New Object]
E --> F[Store Static Instance]
F --> D
Where Singleton is Used?
Singleton is commonly used for:
- Logger
- Configuration Manager
- Cache Manager
- Database Connection Manager
- Thread Pool Manager
- Application Context
- Object Registry
Basic Singleton Structure
classDiagram
class Singleton {
- static Singleton instance
- Singleton()
+ static getInstance() Singleton
}
Key Rules
A Singleton class should have:
- Private constructor
- Static instance variable
- Public static method to return instance
Simple Singleton Example
public class AppConfig {
private static AppConfig instance;
private AppConfig() {
System.out.println("AppConfig object created");
}
public static AppConfig getInstance() {
if (instance == null) {
instance = new AppConfig();
}
return instance;
}
public void showConfig() {
System.out.println("Application configuration loaded");
}
}
Main Class
public class SingletonDemo {
public static void main(String[] args) {
AppConfig config1 = AppConfig.getInstance();
AppConfig config2 = AppConfig.getInstance();
config1.showConfig();
System.out.println(config1 == config2);
}
}
Output
AppConfig object created
Application configuration loaded
true
Output Explanation
The message:
AppConfig object created
prints only once.
Even though we called:
AppConfig.getInstance();
two times, only one object was created.
The result:
true
means both references point to the same object.
Object Flow
sequenceDiagram
participant Main
participant AppConfig
Main->>AppConfig: getInstance()
AppConfig-->>Main: Create and return object
Main->>AppConfig: getInstance()
AppConfig-->>Main: Return existing object
Problem With Basic Singleton
The above implementation is not thread-safe.
In a multi-threaded application, two threads may create two objects at the same time.
Thread Safety Problem
flowchart TD
A[Thread 1 calls getInstance]
B[Thread 2 calls getInstance]
A --> C[instance is null]
B --> D[instance is null]
C --> E[Create Object 1]
D --> F[Create Object 2]
This breaks the Singleton rule.
Thread-Safe Singleton
public class ThreadSafeAppConfig {
private static ThreadSafeAppConfig instance;
private ThreadSafeAppConfig() {
System.out.println("ThreadSafeAppConfig object created");
}
public static synchronized ThreadSafeAppConfig getInstance() {
if (instance == null) {
instance = new ThreadSafeAppConfig();
}
return instance;
}
}
Problem With synchronized Method
The synchronized method is thread-safe but may reduce performance.
Every call locks the method, even after the object is already created.
Double Checked Locking Singleton
public class DatabaseConnectionManager {
private static volatile DatabaseConnectionManager instance;
private DatabaseConnectionManager() {
System.out.println("DatabaseConnectionManager object created");
}
public static DatabaseConnectionManager getInstance() {
if (instance == null) {
synchronized (DatabaseConnectionManager.class) {
if (instance == null) {
instance = new DatabaseConnectionManager();
}
}
}
return instance;
}
public void connect() {
System.out.println("Database connected successfully");
}
}
Main Class
public class DatabaseDemo {
public static void main(String[] args) {
DatabaseConnectionManager db1 =
DatabaseConnectionManager.getInstance();
DatabaseConnectionManager db2 =
DatabaseConnectionManager.getInstance();
db1.connect();
System.out.println(db1 == db2);
}
}
Output
DatabaseConnectionManager object created
Database connected successfully
true
Why volatile is Used?
private static volatile DatabaseConnectionManager instance;
volatile ensures that all threads see the latest value of the instance variable.
It prevents visibility issues in multi-threaded environments.
Best Singleton Approach in Java
Enum Singleton is considered one of the safest Singleton implementations.
public enum LoggerService {
INSTANCE;
public void log(String message) {
System.out.println("LOG: " + message);
}
}
Main Class
public class EnumSingletonDemo {
public static void main(String[] args) {
LoggerService logger1 = LoggerService.INSTANCE;
LoggerService logger2 = LoggerService.INSTANCE;
logger1.log("Application started");
System.out.println(logger1 == logger2);
}
}
Output
LOG: Application started
true
Why Enum Singleton is Better?
Enum Singleton protects against:
- Reflection issues
- Serialization issues
- Multiple object creation
- Thread safety problems
Real-Time Example: Logger
In enterprise applications, we usually need one common logger.
public class ApplicationLogger {
private static final ApplicationLogger INSTANCE = new ApplicationLogger();
private ApplicationLogger() {
}
public static ApplicationLogger getInstance() {
return INSTANCE;
}
public void info(String message) {
System.out.println("[INFO] " + message);
}
public void error(String message) {
System.out.println("[ERROR] " + message);
}
}
Logger Usage
public class OrderService {
public void createOrder() {
ApplicationLogger logger = ApplicationLogger.getInstance();
logger.info("Order created successfully");
}
}
Output
[INFO] Order created successfully
Framework Examples
Spring Framework
By default, Spring Beans are Singleton scoped.
@Service
public class PaymentService {
public void processPayment() {
System.out.println("Payment processed");
}
}
Spring creates only one instance of PaymentService by default.
Spring Singleton Scope
flowchart LR
A[Spring Container]
A --> B[PaymentService Bean]
C[Controller 1] --> B
D[Controller 2] --> B
E[Controller 3] --> B
All controllers reuse the same Spring Bean instance.
Spring Example
@RestController
public class PaymentController {
private final PaymentService paymentService;
public PaymentController(PaymentService paymentService) {
this.paymentService = paymentService;
}
@GetMapping("/pay")
public String pay() {
paymentService.processPayment();
return "Payment Success";
}
}
Important Spring Note
Spring Singleton is not exactly the same as GoF Singleton.
GoF Singleton:
One object per JVM classloader
Spring Singleton:
One bean per Spring ApplicationContext
Java Framework Examples
| Framework | Singleton Usage |
|---|---|
| Spring | Default Bean Scope |
| Hibernate | SessionFactory |
| Log4j | Logger Instance |
| Runtime | Runtime.getRuntime() |
| Cache Managers | Shared Cache Object |
Java Runtime Singleton Example
Runtime runtime = Runtime.getRuntime();
Runtime provides access to JVM runtime environment using Singleton-style access.
Singleton in Microservices
In microservices, Singleton can be used inside one service instance for:
- Config cache
- In-memory lookup data
- Metrics registry
- Local object registry
But remember:
Singleton is per JVM, not across multiple pods or servers.
Kubernetes Example
If one microservice has 3 pods:
Order Service Pod 1 → Singleton Object 1
Order Service Pod 2 → Singleton Object 2
Order Service Pod 3 → Singleton Object 3
Singleton does not mean one object across the entire distributed system.
Microservices Diagram
flowchart TD
A[Load Balancer]
A --> B[Order Service Pod 1<br/>Singleton Instance]
A --> C[Order Service Pod 2<br/>Singleton Instance]
A --> D[Order Service Pod 3<br/>Singleton Instance]
Benefits
- Controls object creation
- Saves memory
- Provides global access
- Useful for shared resources
- Simple to implement
- Good for configuration and logging
Limitations
- Can make unit testing harder
- Can introduce global state
- Not ideal for distributed systems
- Can hide dependencies
- Thread safety must be handled carefully
When to Use Singleton
Use Singleton when:
- Only one instance is needed
- Object creation is expensive
- Shared access is required
- The object is stateless or safely manages state
Examples:
- Logger
- Configuration Manager
- Cache Manager
When Not to Use Singleton
Avoid Singleton when:
- Object holds user-specific state
- Application needs multiple instances
- It makes testing difficult
- You are trying to share data across microservices
Interview Questions
What is Singleton Pattern?
Singleton ensures that only one instance of a class is created and provides global access to it.
Why is constructor private?
To prevent object creation using new.
Is Singleton thread-safe?
Basic Singleton is not thread-safe.
Use synchronized, double-checked locking, or enum Singleton.
What is the best Singleton implementation in Java?
Enum Singleton is considered safest.
Is Spring Bean Singleton same as GoF Singleton?
No.
Spring Singleton means one bean per Spring ApplicationContext.
GoF Singleton means one instance per JVM classloader.
Key Takeaways
- Singleton is a Creational Design Pattern.
- It restricts object creation to one instance.
- Private constructor prevents external object creation.
getInstance()provides global access.- Enum Singleton is safest in Java.
- Spring Beans are singleton by default.
- In microservices, Singleton is per service instance, not global across pods.