Saturday, September 5, 2009

Singleton Objects

Singleton objects:

The normal way in which we create an object in java is using the ‘new’ keyword along with the proper constructor. In that way, each time an object is created, we get a new one. However, we may come under scenarios sometimes where we need the same object whenever we access it. That is the time we actually go for creating a singleton object. One very common example is when we have license for only one connection for our database or our JDBC driver has trouble with multi threading. In that case, Singleton ensures that only one connection is made or that only one thread can access to the connection object at any time.

In definitive terms, we can say that a Singleton is an object that can be created but cannot be instantiated by the developers. It does mean that a singleton object has control over how it is created. The restriction on a singleton is that there can be only one instance of a singleton created by the Java Virtual Machine (JVM) and by preventing direct instantiating; we ensure that developers cannot create a second copy.

Let’s take a look at the following example for better understanding of the singleton objects:

public class SingletonExample {

private static SingletonExample ref;
private String name;

private SingletonExample() {

}


public static synchronized SingletonExample getSingletonExample() {
if(ref == null)
ref = new SingletonExample();
return ref;
}


public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}


public static void main(String[] args) {
SingletonExample singletonExample =
SingletonExample.getSingletonExample();
singletonExample.name = "Varun";
SingletonExample singletonExample2 =
SingletonExample.getSingletonExample();
System.out.println(singletonExample2.name);
}
}


If we look at the above example, we find that following are the different rules that need to be applied to make an object singleton.

Rule #1: We should create a default ‘private constructor’. Private constructors ensure that there is no way that an object can be created (instantiated) form outside of the same class.
private SingletonExample() {

}

Rule #2: Create a private static member variable of the class and a public accessor method that returns the singleton objects and does not create a new one every time it is called:
private static SingletonExample ref;
public static SingletonExample getSingletonExample() {
if(ref == null)
ref = new SingletonExample();
return ref;
}

If you look at the method, you will see that when it is called, we first check if the reference is null (which will be the case, when it is called first time) and if it is null, we create the object i.e. instantiate it using the constructor. Note that we can create the object by calling the private constructor here since we have this method in the same class itself and hence have access to private methods/variables/constructors too. If it is however, not null, it means it is already created and hence we return the same instance.
Also, since the member variable ‘ref’ is static, a same copy of it is returned every time allowing the method to return singleton instance.

Rule #3: Mark the accessor method that returns the singleton object as ‘synchronized’: Rule 1 and 2 ensures that the object returned is singleton. Well, not exactly. Imagine what if you have two separate threads say, Thread A and Thread B running and both the threads gets access to the accessor method we created above at the same time. In that case, we still end up in creating two different objects (one by Thread A and other by Thread B). To prevent this, we mark the accessor method as ‘synchronized’ so that only one thread is allowed to access it at any given point of time and hence multithreading also does not ends up in creating 2 different references of a singleton object.
public static synchronized SingletonExample getSingletonExample() {
if(ref == null)
ref = new SingletonExample();
return ref;
}

Rule #4: If we think, there is one more problem that can make our object deviate from its singleton rule. Well, consider what if I clone the already created the singleton object as:
SingletonExample obj = SingletonExample.getSingletonExample();
SingletonExample clone = (SingletonExample) obj.clone();

In the above way, we cloned the already created singleton object and hence ended up in having 2 different copies of the object to play with (1 is the singleton and other one is the cloned one). This violates our singleton principle. So, to prevent this, we need to add the clone() method and make sure that we cannot clone any object of this class by throwing the CloneNotSupportedException.

public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}

Now run the above example. You will find that the name variable is assigned the value of ‘Varun’ even for the second object that is created. This is because we have created a singleton object i.e. a single object is returned every time and hence all the instance variables values are shared.

No comments:

Post a Comment