Having used the Singleton, a creational design pattern so often, a lot about it has not been very obvious. So here is my attempt to put together everything about singleton.
Here's what the GoF declares as the intent of singleton pattern:
The Singleton pattern ensures only one class instance and provides a global point of access to it.
Sample Java code for the simplest Singleton:
package demo;
public class Singleton {
private static Singleton instance = null;
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
private Singleton() {
}
}
As the simplest implementation, the above code cares nothing about thread-safety, serialization etc.,
But then when we need to put together everything about singleton,
we are indeed going to care about those. ;-)
Thread-safety:
The getInstance() method is not thread-safe, which means it is not possible that the class can be called by multiple threads in parallel and assumed only one object will be created.
The simplest fix would be to create the instance as part of the declaration itself.
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
Here we face the biggest problem of early initialization. Even if the getInstance() is never going to be called (wonder why in the world, one would decide to keep such an object as singleton? ;-) ), just loading the class would result in the object creation, a memory overhead in case the object is huge (as in most of the times).
An alternative could be to declare the getInstance() method as synchronized. But then, it's going to create a performance overhead due to lock contention. With the API exposed to the whole world and expectedly hundreds of calls to retrieve the singleton instance, synchronized wouldn't be the best option.
By then, we come across a clever idea, the "Double Checked Locking" to overcome the disadvantages of these techniques. Using DCL, the instance is not created as part of the declaration and the method is not declared as synchronized either.
public static Singleton getInstance() {
if (instance == null) {
synchronized(Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
Looks interesting, isn't it? Quite unfortunately, the DCL just doesn't help (at least in theory, yeah I have failed miserably trying to reproduce the problem).
The link below explains the theory:
We could now think of a combination of early object creation and DCL based resource initialization as an alternative (see below). But since I haven't reproduced the DCL failure yet, I can't be really sure if this would help.
package demo;
public class Singleton {
private static boolean initialized = false;
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
if (!initialized) {
synchronized(Singleton.class) {
if (!initialized) {
instance.runHeavyInit();
}
}
}
return instance;
}
private Singleton() {
}
private synchronized void runHeavyInit() {
if (!initialized) {
doHeavyInit();
initialized = true;
}
}
private void doHeavyInit() {
// Do things here
}
}
And java.util.concurrent.locks.ReadWriteLock has been hogging my mind, looking to help as another alternative.
Serialization:
Fortunately, there is no problem at all while serializing a Singleton instance. But de-serialization is a nightmare. The de-serialization creates another object, a colleague to the Singleton instance (given getInstance() is called already or early initialization has occurred) defeating the purpose of the whole implementation, but wait, don't we know that "failure is never final" ? ;-)
The Serializable API helps us in defining a "serializable" Singleton. Yeah, finally we found one way to use the rarely used method.
<ANY-ACCESS-MODIFIER> Object readResolve() throws ObjectStreamException {
return instance;
}
We add the above method to our Singleton class, and we are done. The existing instance is used for de-serialization to replace the existing contents with the de-serialized contents.
Finally, there are 2 problems design gurus (of course Developers, during their UT) hate about the Singleton design pattern.
1) A pure Singleton class cannot be sub-classed (due to the private constructor), against a famous design principle, a class should be open for extension.
2) Dependency on a class is injected. Another violation of a design principle, instead of programming to an interface, we must have to program to a concrete implementation.
P.S: I guess this is the first ever explanation on a design pattern without an UML diagram depicting it. :-)