Monday, November 30, 2009

Anything perfect is never simple, even a Singleton !

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. :-)

2 comments:

  1. I happened to read this just today when I clicked on your status today ;) This seemed an interesting read and so my two cents:

    How about this:

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

    As for your second issue of the instance getting instantiated even when the class is loaded, you could just have an inner class that creates the instance on load, but the class itself can get loaded on the outer class's getInstance().

    ReplyDelete
  2. In theory, we need the synchronization only until the object gets created for the first time. After that the synchronization is a mere overhead. Given the conventional use of singletons (1000s of getInstance() invocations), the overhead may end up being significant. So, using synchronized may not be the best option :)

    ReplyDelete