Singleton Design Pattern and 7 Ways to Implement It 4 Ways to Break Singleton Design Pattern
Categories: Java Java Examples Java Design Pattern
Singleton Design Pattern and 7 Ways to Implement It 4 Ways to Break Singleton Design Pattern
Singleton solves Problem -In software development, we often require classes that can only have one object for a JVM . For example in thread pools, caches, loggers etc.
What is the Singleton Design 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.Singleton involves only one class which is responsible for instantiating itself, making sure it creates not more than one instance.
Implementation
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 public method for external objects to access the singleton object.
This can be achieved by making the constructor private and providing a public static method for external objects to access it.
The instance class variable holds the one and only instance of the SingletonClassName class.
The SingletonClassName() constructor is declared as private, preventing external objects from creating new instances.
The getInstance() method is a static class method, making it public so that it is accessible to the external world.
There are 7 ways to implement the Singleton Pattern, each with its own advantages and disadvantages.
- Lazy Initialization[ Never used -Break By Multi threaded]
1-Break :- How to Break Lazy Initialization Singleton?
2-Break Lazy Initialization using reflection
3.Check :-How to stop breaking singleton design patterns using reflection?
4.Break:- how to break singleton design pattern using serializable in java
5.Check:-How to stop break singleton design pattern using serializable in java
6.Break Singleton :- How to stop break singleton design pattern using clone in java
7.Check:-Prevent Singleton Pattern From Cloning - Thread-Safe Singleton
- Double-Checked Locking
- Eager Initialization
- Bill Pugh Singleton
- Enum Singleton
- Static Block Initialization
1. Lazy Initialization
This approach creates the singleton instance only when it is needed, saving resources if the singleton is never used in the application.
package r4r.co.in.singleton.lazy;
public class LazyInitializationSingleton {
private static LazyInitializationSingleton INSTANCE;
private LazyInitializationSingleton() {
System.out.println("I am in Singleton class....");
}
public static LazyInitializationSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazyInitializationSingleton();
}
return INSTANCE;
}
}
- Checks if an instance already exists (instance == null).
- If not, it creates a new instance.
- If an instance already exists, it skips the creation step.
This implementation is not thread-safe. If multiple threads call getInstance() simultaneously when an instance is null, it's possible to create multiple instances.
How to Break Lazy Initialization Singleton?
Break Lazy Initialization Singleton Using MultiThreading- Here two threads created you can run code and see two time constructors initialized. You know construct initialized by JVM only when a new object is created .Hence proved Singleton break in case of multithreaded aspects.
package r4r.co.in.singleton.lazy;
public class BreakLazyInitializationSingleton {
public static void main(String[] args) {
//Break Lazy Initialization Singleton Using MultiThreading
Thread thread1 = new Thread(() -> {
LazyInitializationSingleton singleTonObj1
= LazyInitializationSingleton.getInstance();
});
Thread thread2 = new Thread(() -> {
LazyInitializationSingleton singleTonObj2
= LazyInitializationSingleton.getInstance();
});
thread1.start();
thread2.start();
}
}
Output:-[Some time]
I am in Singleton class....
I am in Singleton class....
Break Lazy Initialization using reflection :-
As below example
package r4r.co.in.singleton.lazy.breaksingleton;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import r4r.co.in.singleton.lazy.LazyInitializationSingleton;
public class BreakLazyInitializationUsingReflaction {
// Break Lazy Initialization using reflection
@SuppressWarnings("unchecked")
public static void main(String[] args) throws ClassNotFoundException,
NoSuchMethodException, SecurityException,
InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
LazyInitializationSingleton instance = LazyInitializationSingleton.getInstance();
Class<?> singleTonClass = Class.forName("r4r.co.in.singleton.lazy.LazyInitializationSingleton");
Constructor<LazyInitializationSingleton> constructorOfSingleTon = (Constructor<LazyInitializationSingleton>
) singleTonClass
.getDeclaredConstructor();
constructorOfSingleTon.setAccessible(true);
// constructorOfSingleTon.newInstance();
LazyInitializationSingleton instanceReflection = constructorOfSingleTon.newInstance();
System.out.println(instanceReflection);
System.out.println(instance);
}
}
Output
I am in Singleton class....
I am in Singleton class....
r4r.co.in.singleton.lazy.LazyInitializationSingleton@1936f0f5
r4r.co.in.singleton.lazy.LazyInitializationSingleton@6615435c
How to stop break singleton design pattern using reflection?
Prevent Break SIngleton :-Within the private constructor of your singleton class, add a check to see if an instance already exists and throw an exception if it does
.
package r4r.co.in.singleton.lazy;
public class LazyInitializationSingleton {
private static LazyInitializationSingleton INSTANCE;
private LazyInitializationSingleton() {
if (INSTANCE != null) {
throw new IllegalStateException("Singleton already exists");
}
INSTANCE = this;
System.out.println("I am in Singleton class....");
}
public static LazyInitializationSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazyInitializationSingleton();
}
return INSTANCE;
}
}
Break:- how to break singleton design pattern using serializable in java
Change in Class :- Need to implements Serializable
package r4r.co.in.singleton.lazy;
import java.io.Serializable;
public class LazyInitializationSingleton implements Serializable{
private static final long serialVersionUID = 1L;
private static LazyInitializationSingleton INSTANCE;
private LazyInitializationSingleton() {
System.out.println("I am in Singleton class....");
}
public static LazyInitializationSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazyInitializationSingleton();
}
return INSTANCE;
}
}
Break:- how to break singleton design pattern using serializable in java
package r4r.co.in.singleton.lazy.breaksingleton;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import r4r.co.in.singleton.lazy.LazyInitializationSingleton;
public class BreakSingletonUsingSerializable {
public static void main(String[] args)
throws FileNotFoundException
, IOException, ClassNotFoundException
{
// Break Singleton Using Serializable
LazyInitializationSingleton initializationSingleton=
LazyInitializationSingleton.getInstance();
ObjectOutputStream objectOutputStream=new ObjectOutputStream(new
FileOutputStream ("d://serializable.ser"))
;
objectOutputStream.writeObject(initializationSingleton);
objectOutputStream.close();
ObjectInputStream inputStream=new ObjectInputStream(new FileInputStream
("d://serializable.ser"));
LazyInitializationSingleton serializableSingleton=(LazyInitializationSingleton) inputStream.readObject();
System.err.println(serializableSingleton.hashCode());
System.err.println(initializationSingleton.hashCode());
}
}
Output:-
I am in Singleton class....
644166178
1651945012
How to stop break singleton design pattern using serializable in java
To prevent breaking the Singleton pattern when using serialization in Java, you need to implement the readResolve() method in your Singleton class. This method ensures that the same instance is returned after deserialization.
readResolve is an private method used in ObjectInputStream while deserializing an object.
package r4r.co.in.singleton.lazy;
import java.io.Serializable;
public class LazyInitializationSingleton implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private static LazyInitializationSingleton INSTANCE;
private LazyInitializationSingleton() {
System.out.println("I am in Singleton class....");
}
public static LazyInitializationSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new LazyInitializationSingleton();
}
return INSTANCE;
}
// Method to handle deserialization
private Object readResolve() {
return INSTANCE;
}
}
Output ➖
I am in Singleton class....
1651945012
1651945012
Break Singleton using clone:- How to stop break singleton design pattern using clone in java
package r4r.co.in.singleton.lazy;
public class LazyInitializationSingleton implements Cloneable {
private static LazyInitializationSingleton INSTANCE;
private LazyInitializationSingleton() {
System.out.println("I am in Singleton class....");
}
public static LazyInitializationSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new
LazyInitializationSingleton();
}
return INSTANCE;
}
@Override
public Object clone()
throws CloneNotSupportedException
{
return super.clone();
}
}
package r4r.co.in.singleton.lazy.breaksingleton;
import r4r.co.in.singleton.lazy.LazyInitializationSingleton;
public class BreakSingletonUsingClone {
public static void main(String[] args) throws CloneNotSupportedException
{
LazyInitializationSingleton initializationSingleton=LazyInitializationSingleton.getInstance();
LazyInitializationSingleton clonedSingleton=(LazyInitializationSingleton) initializationSingleton.clone();
System.err.println(initializationSingleton.hashCode());
System.err.println(clonedSingleton.hashCode());
}
}
Prevent Singleton Pattern From Cloning:-
Override clone() and throw new CloneNotSupportedException() or return INSTANCE; .It will resolve singleton break issue using clone
package r4r.co.in.singleton.lazy;
public class LazyInitializationSingleton implements Cloneable {
private static LazyInitializationSingleton INSTANCE;
private LazyInitializationSingleton() {
System.out.println("I am in Singleton class....");
}
public static LazyInitializationSingleton getInstance() {
if (INSTANCE == null) {
INSTANCE = new
LazyInitializationSingleton();
}
return INSTANCE;
}
@Override
public Object clone()
throws CloneNotSupportedException
{
// return INSTANCE;
throw new CloneNotSupportedException();
}
}
2. Thread-Safe Singleton
This approach is similar to lazy initialization but also ensures that the singleton is thread-safe.
This is achieved by making the getInstance() method synchronized ensuring only one thread can execute this method at a time.
When a thread enters the synchronized method, it acquires a lock on the class object. Other threads must wait until the method is executed.
package r4r.co.in.singleton.lazy;
public class ThreadSafeLazyInitializationSingleton {
//Thread-Safe Singleton
private ThreadSafeLazyInitializationSingleton() {
System.out.print("I am in Singleton Class");
}
private static ThreadSafeLazyInitializationSingleton INSTANCE;
public static synchronized ThreadSafeLazyInitializationSingleton getInstance() {
if(INSTANCE==null) {
INSTANCE=new ThreadSafeLazyInitializationSingleton();
}
return INSTANCE;
}
}
- The synchronization keyword ensures that only one thread can perform the (instance == null) check and create the object.
If calling the getInstance() method isn’t causing substantial overhead, this approach is straightforward and effective.
But, using synchronized can decrease performance, which can be a bottleneck if called frequently.
3. 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.
package r4r.co.in.singleton.lazy;
public class DoubleCheckedLocking {
// Double-Checked Locking
private DoubleCheckedLocking() {
System.out.print("Double Checked Locking");
}
//The volatile keyword to ensure that changes to the instance variable are immediately visible to other threads.
private static volatile DoubleCheckedLocking INSTANCE;
public static DoubleCheckedLocking getInstance() {
if (INSTANCE == null)
synchronized (DoubleCheckedLocking.class) {
if (INSTANCE == null) {
INSTANCE = new DoubleCheckedLocking();
}
}
return INSTANCE;
}
}
- If the first check (instance == null) passes, we synchronize on the class object.
- We check the same condition one more time because multiple threads may have passed the first check.
- The instance is created only if both checks pass.
Although this method is a bit complex to implement, it can drastically reduce the performance overhead.
4. Eager Initialization
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 accesses the instance variable.
This implementation is one of the simplest and inherently thread-safe without needing explicit synchronization.
package r4r.co.in.singleton.eager;
public class EagerInitialization {
//Eager Initialization -Singleton Example.
private final static EagerInitialization INSTANCE= new EagerInitialization();
private EagerInitialization() {
System.out.print("EagerInitialization");
}
public static EagerInitialization getInstance() {
return INSTANCE;
}
}
- static variable ensures there's only one instance shared across all instances of the class.
- final prevents the instance from being reassigned after initialization.
This approach is suitable if your application always creates and uses the singleton instance, or the overhead of creating it is minimal.
While it is inherently thread-safe, it could potentially waste resources if the singleton instance is never used by the client application.
5. Bill Pugh Singleton
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.
package r4r.co.in.singleton.eager;
public class BillPughSingleton {
private BillPughSingleton() {
System.out.print("Bill Pugh Singleton......");
}
private static class SingletonHelper {
private static final BillPughSingleton INSTANCE = new BillPughSingleton
();
}
public static BillPughSingleton getInstance() {
return SingletonHelper.INSTANCE;
}
}
- When the getInstance() method is called for the first time, it triggers the loading of the SingletonHelper class.
- When the inner class is loaded, it creates the INSTANCE of BillPughSingleton.
- The final keyword ensures that the INSTANCE cannot be reassigned.
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.
6. Enum Singleton
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.
package r4r.co.in.singleton.enumsintone;
enum EnumSingleton {
INSTANCE;
public void doSomthing(){
}
}
Many Java experts, including Joshua Bloch, recommend Enum Singleton as the best singleton implementation in Java.
However, it's not always suitable, especially if you need to extend a class or if lazy initialization is a strict requirement.
7. Static Block 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.
package r4r.co.in.singleton.eager;
public class StaticBlockInitializationSingleton {
//static block Initialization
private StaticBlockInitializationSingleton() {
System.out.print("Singleton using Static Block Initialization...");
}
private static StaticBlockInitializationSingleton INSTANCE;
static {
try {
INSTANCE = new StaticBlockInitializationSingleton();
} catch (RuntimeException e) {
throw new RuntimeException("Exception while creating singleton ");
}
}
public StaticBlockInitializationSingleton getInstance() {
return INSTANCE;
}
}
- The static block is executed when the class is loaded by the JVM.
- If an exception occurs, it's wrapped in a RuntimeException.
It is thread safe but not lazy-loaded, which might be a drawback if the initialization is resource-intensive or time-consuming.
Real-World Examples of Singleton Design Pattern
Singleton is useful in scenarios like:
- Managing Shared Resources (database connections, thread pools, caches, configuration settings)
- Coordinating System-Wide Actions (logging, print spoolers, file managers)
- Managing State (user session, application state)
Specific Examples:
- Logger Classes: Many logging frameworks use the Singleton pattern to provide a global logging object. This ensures that log messages are consistently handled and written to the same output stream.
- Database Connection Pools: Connection pools help manage and reuse database connections efficiently. A Singleton can ensure that only one pool is created and used throughout the application.
- Cache Objects: In-memory caches are often implemented as Singletons to provide a single point of access for cached data across the application.
- Thread Pools: Thread pools manage a collection of worker threads. A Singleton ensures that the same pool is used throughout the application, preventing resource overuse.
- File System: File systems often use Singleton objects to represent the file system and provide a unified interface for file operations.
- Print Spooler: In operating systems, print spoolers manage printing tasks. A single instance coordinates all print jobs to prevent conflicts.
Pros and Cons of Singleton Design Pattern
Pros |
Cons |
Ensures a single instance of a class and provides a global point of access to it |
Violates the SIngle Responsibility Principle:- The pattern solves two problems at the same time . |
Only one object is created ,which can be particularly beneficial for resource heavy classes . |
In multithreaded environments ,special care must be taken to implement Singletons correctly to avoid race conditions . |
Provides a way to maintain global state within an application |
Introduces global state into an application,which might be difficult to manage . |
Supports lazy loading ,where the instance is only created when it’s first needed |
Classes using the singleton can become tightly coupled the singleton class |
Guarantees that every object in the application uses the same global resource. |
Singleton patterns can make unit testing difficult due to the global state it introduces |