All you want to know about Singleton
The singleton design pattern is one of the most inappropriately used patterns. In this article we review several implementations of a singleton that work correctly in multithreaded environment, with serialization and cloning tasks and even with reflection attacks.
Contents
- What for you can use singleton
- Distinction from static class
- Lazy or eager loading singleton?
- Eager loading singleton
- Rough synchronization
- Double-checked locking singleton
- Initialization-on-demand holder idiom
- The enum based singleton
- Problems with serialization and deserialization
- Problems with clone
- Problems with reflection
- So, why you can think about singleton as anti-pattern?
- Conclusion
What for you can use singleton
The singleton pattern is used when there must be exactly one instance of a class, and it must be accessible to clients from a well-known access point or when the single instance should be extensible by sub-classes, and clients should be able to use an extended instance without modifying their code.
Distinction from static class
JDK has examples of both singleton and static, on the one hand java.lang.Math
is a final class with static methods, on the other hand java.lang.Runtime
is a singleton class.
Advantages of singleton
- If your need to maintain state than singleton pattern is better choice than static class, because maintaining state in static class leads to bugs, especially in concurrent environment, that could lead to race conditions without adequate synchronization parallel modification by multiple threads.
- Singleton class can be lazy loaded if its a heavy object, but static class doesn't have such advantages and always eagerly loaded.
- With singleton, you can use inheritance and polymorphism to extend a base class, implement an interface and provide different implementations.
- Since static methods in Java cannot be overridden, they lead to inflexibility. On the other hand, you can override methods defined in singleton class by extending it.
Disadvantages of static class
- It is easier to write unit test for singleton than static class, because you can pass mock object whenever singleton is expected.
Advantages of static class
- Static class provides better performance than singleton, because static methods are bonded on compile time.
Lazy or eager loading singleton?
While implementing singleton we have two options:
- Eager loading — early initialization, create an instance when initialize.
- Lazy loading — create an instance when required.
Lazy loading is used when you need to delay initialization of a very large object or computation a heavy construction code and also have other accessible static methods or fields that might be used before an instance is needed, then and only then you need to use lazy initialization. Otherwise, choosing early loading is a good choice.
Eager loading singleton
if early loading is suitable to you — do it
This way has a number of advantages:
- There is no need to synchronize the
getInstance
method, because all threads will see the same instance and no expensive locking is required. - The static initializer is run when the class is initialized, after class loading but before the class is used by any thread.
- The final keyword means that the instance cannot be redefined, ensuring that one and only one instance ever exists.
private static final Singleton INSTANCE = new Singleton();
Static initialization is also available if needed. The class loader does exactly the same processing:
private static final Singleton INSTANCE;
static {
try {
INSTANCE = new Singleton();
} catch (Exception e) {
throw new RuntimeException("An error occurred!", e);
}
}
Notice that the constructor is made private to exclude the ability to make an instance of the class outside the Singleton class:
private Singleton() {}
Factory method for singleton instance, if you want to protect inner realization for further development or modification (for example change lazy to eager loading and vice verse):
public static Singleton getInstance {
return INSTANCE;
}
Eager initialization is less complicated and chances of making a mistake is relatively less in early loading as the object is created in advance and ready to use when requested. As mentioned before if early loading is suitable to you, just do it.
Below other examples for lazy loading singleton and you will see how they are more difficult.
Rough synchronization
the easiest way with serious performance issues
In a multi-threaded environment we need to be sure that only one thread can execute the getInstance
method at a given time. The easiest way to do this for lazy loading singleton is to use the synchronized
keyword for the method.
From performance standpoint this code is inefficient and has an unnecessary overhead.
public class Singleton {
private static Singleton instance = null;
private Singleton () {}
public static synchronized Singleton getInstance {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
In general — do not use it in serious projects.
Double-checked locking singleton
well-known version with unfamiliar issues
Double checked locking is a technique to prevent creating another instance of singleton when call to getInstance
method is made in multithreading environment.
Pay attention
- Singleton instance is checked twice before initialization.
- Synchronized critical section is used only after first checking singleton instance for that reason to improve performance.
volatile
keyword on the declaration of the instance member. This will tell the compiler to always read from, and write to, main memory and not the CPU cache. Withvolatile
variable guaranteeing happens-before relationship, all the write will happen before any read of instance variable.
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
Disadvantages
- Since it requires the
volatile
keyword to work properly, it's not compatible with Java 1.4 and lower versions. The problem is that an out-of-order write may allow the instance reference to be returned before the singleton constructor is executed. - Performance issue because of decline cache for volatile variable.
- Singleton instance is checked two times before initialization.
- It's quite verbose and it makes the code difficult to read.
Initialization-on-demand holder idiom
the best realization of lazy loading singleton pattern
- It is as lazy as possible, and works in all known versions of Java.
- It takes advantage of language guarantees about class initialization, and will therefore work correctly in all Java-compliant compilers and virtual machines.
- Without synchronization overhead. Benchmark indicates it is far faster than even uncontended synchronization.
public class Singleton {
private Singleton() {}
public static Singleton getInstance {
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
This is a perfect way to get a singleton using the JVM constraints of class and object initialization. The JVM guarantees that the static class SingletonHolder
is not initialized until the JVM determines that SingletonHolder
must be executed. The static class SingletonHolder
is only executed when the static method getInstance
is invoked on the class Singleton
, and the first time this happens the JVM will load and initialize the SingletonHolder
class.
Since the JLS (Java Language Specification) guarantees the class initialization phase is to be non-concurrent, no further synchronization is required in the static getInstance
method during loading and initialization.
The enum based singleton
a modern look at an old problem
This approach implements the singleton by taking advantage of Java's guarantee that any enum value is instantiated only once in a Java program and enum provides implicit support for thread safety. Since Java enum values are globally accessible, so they can be used as a singleton.
public enum Singleton {
SINGLETON;
public void method() { }
}
How does this work? Well, line two of the code may be considered to something like this:
public final static Singleton SINGLETON = new Singleton();
And we get good old early initialized singleton.
Remember that since this is an enum you can always access to the instance via Singleton.SINGLETON
as well:
Singleton s = Singleton.SINGLETON;
Advantages
- To prevent creating other instances of singleton during deserialization use enum based singleton because serialization of enum is taken care by JVM. Enum serialization and deserialization work differently than for normal java objects. The only thing that gets serialized is the name of the enum value. During the deserialization process the enum
valueOf
method is used with the deserialized name to get the desired instance. - Enum based singleton allows to protect itself from reflection attacks. The enum type actually extends the java
Enum
class. The reason that reflection cannot be used to instantiate objects of enum type is that the java specification disallows and that rule is coded in the implementation of thenewInstance
method of theConstructor
class, which is usually used for creating objects via reflection:
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
- Enum is not supposed to be cloned because there must be exactly one instance of each value.
- The most laconic code among all singleton realizations.
Disadvantages
- The enum based singleton does not allow lazy initialization.
- If you changed your design and wanted to convert your singleton to multiton, enum would not allow this. The multiton pattern is used for the controlled creation of multiple instances, which it manages through the use of a
map
. Rather than having a single instance per application (e.g. thejava.lang.Runtime
) the multiton pattern instead ensures a single instance per key. - Enum appears only in Java 5 so you can not use it in the prior version.
Problems with serialization and deserialization
One of the problems with conventional singletons (not enum based) is that once you implement Serializable
interface they are no longer remain singleton because readObject
method always return a new instance like constructor in Java.
For Serializable
and Externalizable
classes, the readResolve
method allows a class to replace the object read from the stream before it is returned to the caller. By implementing the readResolve
method, a class can directly control the types and instances of deserialized objects. The readResolve
method is called when ObjectInputStream
has read an object from the stream and is preparing it for returning to the caller.
public class Singleton implements Serializable {
// skipped...
protected Object readResolve() throws ObjectStreamException {
return INSTANCE;
}
}
Or you can use enum based singleton to create singleton because serialization of enum is taken care by JVM.
Problems with clone
Using the clone
method, we can create a copy of the original object. The same thing happens if we applied clone
to conventional singletons (not enum based).
To overcome the above issue, we need to override the clone
method and throw an exception CloneNotSupportedException
.
public class Singleton {
// skipped...
@Override
protected Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException("Not permitted to clone Singleton");
}
}
Problems with reflection
One of the solutions of reflection problem with conventional singletons (not enum based) is to throw a run-time exception in the constructor if the instance already exists. So we will not allow the creation of a second instance.
private Singleton() {
throw new IllegalStateException("Not permitted to construct Singleton by reflection");
}
Or you can use enum based singleton to create singleton because reflection cannot be used to instantiate objects of enum type.
By the way, why you can think about singleton as anti-pattern?
Why you should avoid the singleton anti-pattern at all and replace it with DI (Dependency Injection)? For example, every class that needs access to a shared object gets the object through its constructors or with a DI-container.
With more and more classes calling getInstance
method the code gets more and more tightly coupled, monolithic, not testable and hard to change and hard to reuse because of not configurable hidden dependencies.
Tight coupling
Since the singleton provide a global state to other classes, it is often used everywhere in the code. This makes singletons in your application the central place where all the rest of the code directly or indirectly depends upon. A particular module might be harder to reuse and test because dependent modules must be included.
Difficulties with testing
Since factory method getInstance
is globally accessible, you call it with a class name, instead of depending on an interface you can later change by another realization or mock. That's why it's impossible to replace it when you want to test the method or the class.
Singleton keeps state while the application run
Persistent state is the enemy of unit testing. One of the things that makes unit testing effective is that each test is independent from others. If this is not true, then the order in which the tests run affects the outcome of the tests. This can lead to cases where tests fail when they should not, and even worse, it can lead to tests that pass.
Conclusion
Singleton pattern can be used correctly or wrong just like any other tool. The bad attitude to a singleton is came from the inappropriate usage of a singleton for things it is not designed to do. The biggest mistake is using a singleton as a global variable.
In this tutorial, we focused on how to implement the Singleton pattern in different techniques, with their advantages and disadvantages.
Other language versions
- Русская версия — Все что вы хотели знать о Singleton
3
Comments
Don’t hesitate to comment below if you have any questions. Feel free to join the conversation.
Oleg Poltoratskii Jun 2020
I try to make it possible to copy code from page. Wait a while.
P.S. Code is selected and copied, but selection is not visible.
Nuances of Java development
This blog is about Java development and describes in detail the most interesting topics