Singleton pattern is a design pattern that restricts the instantiation of a class to one object. In Java, the implemention should be done to ensure that only one instance of the class exists per class loader, per JVM. The objective in this post is not to explain much theory for scenarios where you apply Singleton etc or it’s implementation variants (read this for those details) but to explain what it takes to make a Singleton, a Complete Singleton. We all know we can use many variants of the so called Singleton implementations in our code. I wonder if all of us really know if our implementation will still be a ‘Singleton’ in situations where we try to invoke clone() and/or make the class Serializable. The class will no longer be a Singleton.
Let’s consider first the clone() scenario and later, serialization scenario. Let’s say your Singleton implementation is as below:
public final class Singleton implements Serializable { // static Singleton reference is initialised to refer to Singleton instance, when this class is first loaded // so that this instance can be accessible without creating an object and without having to worry about thread // synchronization in case of, lazy loading (where we do a null check inside getInstance()... public static final Singleton instance= new Singleton(); // make it private as an optimization step. // Suppresses instantiation outside this class private Singleton() { } // One instance per class loader of this class, per JVM public static Singleton getInstance(){ return instance; } }
Suppose the client API invokes method like below:
public class Client { ... Singleton instance = Singleton.getInstance(); Singleton clone = (Singleton) instance.clone(); ... }
So, clone() call creates another instance of Singleton in heap!!! But, we wanted to ensure ONLY one instance at any time!!! Singleton loses Singleton-ness property!!!!
To prevent this, you should override clone() like below:
// Prevents Singleton.clone() call by the client public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); }
When we do so, the client code will give a compile-time error (CloneNotSupportedException is a checked exception). Half Done!
Now, let’s say our Singleton implements Serializable(in above class). The above class would no longer be a singleton if the words “implements Serializable” were added to its declaration. It doesn’t matter whether the class uses the default serialized form or a custom serialized form, nor does it matter whether the class provides an explicit readObject() method. Any readObject method, whether explicit or default, returns a newly created instance, which will not be the same instance that was created at class initialization time. As Joshua Bloch mentions in his Effective Java: Prior to the 1.2 release, it was impossible to write a
serializable singleton class. In the 1.2 release, the readResolve() feature was added to the serialization facility. If the class of an object being deserialized defines a readResolve method with the proper declaration, this method is invoked on the newly created object after it is deserialized. The object reference returned by this method is then returned in lieu of the newly created object. In most uses of this feature, no reference to the newly created object is retained; the object is effectively stillborn, immediately becoming eligible for garbage collection.
private Object readResolve() throws ObjectStreamException { // Return the one true Singleton and let the garbage collector // take care of the Singleton impersonator. return instance; }
Note that since the class is final, the contract says that the readResolve() has to be a private method. This method ignores the deserialized object, simply returning the distinguished Singleton instance created when the class was initialized. Therefore the serialized form of a Singleton instance need not contain any real data; all instance fields should be marked transient. This applies not only to this Singleton, but to all singletons.
So here’s the complete singleton code, a complete Singleton Code!!!
public final class Singleton implements Serializable { <pre> // static Singleton reference is initialised to refer to Singleton instance, when this class is first loaded // so that this instance can be accessible without creating an object and without having to worry about thread // synchronization in case of, lazy loading (where we do a null check inside getInstance()... public static final Singleton instance= new Singleton(); // make it private as an optimization step.</p> // Suppresses instantiation outside this class private Singleton() { } // One instance per class loader of this class, per JVM public static Singleton getInstance(){ return instance; } // Prevents Singleton.clone() call by the client public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } private Object readResolve() throws ObjectStreamException { // Return the one true Singleton and let the garbage collector // take care of the Singleton impersonator. return instance; }
Here’s the Lazy Loading variant of the Singleton implementaion using Double-Checked Locking. Based on the Java Memory Model issues, there could be a Just-In-Time (JIT) run-time compiler Threat with this kind of implementation, so explains, Peter Haggar, of Practical Java™ Programming Language Guide fame, here.
</p> public final class Singleton implements Serializable { public static Singleton instance=null; // make it private as an optimization step. // Suppresses instantiation outside this class private Singleton() { } //One instance per class loader of this class, per JVM public static Singleton getInstance(){ if(instance == null) { synchronized(Singleton.class) { if(instance == null) return instance; } } } // Prevents Singleton.clone() call by the client public Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); } private Object readResolve() throws ObjectStreamException { // Return the one true Singleton and let the garbage collector // take care of the Singleton impersonator. return instance; } }