Java Singleton patterns are the simplest type of design patterns. They are part of the Creational Patterns. This type of design pattern is used to ensure that only a single instance of an object is running at all times. The easiest way to do this is simply by making the constructor private and allowing the object to be instantiated only through a public static method.
An example of a simple singleton class is as follows -
This class is pretty self explanatory. The access modifier of the constructor could also be changed to protected in order to allow the children classes to instantiate the singleton class. A method releaseInstance() is used only to forget the current instance of the class.
An example of a simple singleton class is as follows -
public class SingletonClass { private static SingletonClass singleton; private SingletonClass() { //private is used to restrict class instantiantion } public static SingletonClass getInstance() { if (singleton == null) { singleton = new SingletonClass(); } return singleton; } public static void releaseInstance() { singleton = null; } }
This class is pretty self explanatory. The access modifier of the constructor could also be changed to protected in order to allow the children classes to instantiate the singleton class. A method releaseInstance() is used only to forget the current instance of the class.
Ensuring Singletonness
There are some ways in which this implementation of the singleton pattern may break. We look into the problems and ways to ensure the singletonness.
Thread Safety
Sometimes multithreading can break the functionality of singleton classes. In the following test example the singletonness of the class is not maintained.
@Test public void UniqueTest_multithreading() throws InterruptedException { SingletonClass s1; SingletonClass s2; Runnable run = new Runnable() { @Override public void run() { try { SingletonClass s = SingletonClass.getInstance(); System.out.println(s.toString()); //Prints different object instances } catch (Exception e) { e.printStackTrace(); } } }; Thread t1 = new Thread(run, "First"); t1.start(); Thread t2 = new Thread(run, "Second"); t2.start(); t1.join(); t2.join(); }
public static SingletonClass getInstance() { if (singleton == null) { additionalFunctionality(); singleton = new SingletonClass(); } return singleton; } private static void additionalFunctionality() { if (Thread.currentThread().getName().equals("First")) { try { Thread.sleep(100); } catch (InterruptedException ex) { ex.printStackTrace(); } } }
The test essentially creates two threads which individualy call the getInstance() method. Lets say that some additional work is performed for the first thread. In this case, the first thread becomes busy after performing the null check, during which the second thread already intializes the singleton class. After the additional work by the first thread is done, it re-intializes the singleton class. This results in both the threads having two seperate instances of the singleton class.
Solution
A solution for this problem is making the instantiation part of the singleton class synchronized. So, just adding a synchronised block to the getInstance() method will solve this problem.
Solution
A solution for this problem is making the instantiation part of the singleton class synchronized. So, just adding a synchronised block to the getInstance() method will solve this problem.
public static SingletonClass getInstance() { synchronized (SingletonClass.class) { if (singleton == null) { additionalFunctionality(); singleton = new SingletonClass(); } } return singleton; }
Serializing and Deserializing
Another scenario that breaks the singletonness of the class is when the a singleton object is serialized and then deserialized twice. So, the following test would fail.
@Test public void UniqueTest_serializable() { SingletonClass singleton = SingletonClass.getInstance(); writeSingletonObjectToDisk(singleton); SingletonClass s1 = readSingletonObjectFromDisk();
SingletonClass s2 = readSingletonObjectFromDisk();
assertEquals(s1, s2); //Fails }
Solution
There is however a very easy solution for this. The addition of the readResolve() method to the singleton class solves this problem.
What happens here is that the readResolve() method is executed first before beginning the deserialization procedure. When a valid object is found, the deserialization essentially never takes place.
One thing to keep in mind is that for the singleton class to be able to serialize it should implement the interface java.io.Serializable.
There is however a very easy solution for this. The addition of the readResolve() method to the singleton class solves this problem.
private Object readResolve() { return SingletonClass.getInstance(); }
What happens here is that the readResolve() method is executed first before beginning the deserialization procedure. When a valid object is found, the deserialization essentially never takes place.
One thing to keep in mind is that for the singleton class to be able to serialize it should implement the interface java.io.Serializable.
No comments:
Post a Comment