Singleton Pattern: Motivation • Sometimes Need an Object That Is Unique – There Will Only Be a Single Instance of the Class
Total Page:16
File Type:pdf, Size:1020Kb
Singleton Pattern: Motivation • Sometimes need an object that is unique { There will only be a single instance of the class • Could be done with a global variable, BUT { Such an instance would exist for the duration of the program { If the instance were resource-intensive, this could be a problem { It would be better to create such an object only on an as-needed basis 1 Singleton Pattern: Defined • Singleton Pattern (177): Ensures a class has only one instance, and provides a global point of access to it. • The class manages a single instance of itself. • No other classes can create an instance of a singleton on their own. { I.e., another class cannot execute newInstance = new SingletonClass() • The singleton provides the means for other classes to access the instance. • Class diagram: • The question is: How to implement this so that we can insure that no more than one instance can be created? 2 Singleton Pattern: Implementation • We know that if we have a public constructor for a class, instances can be created by any other classes • But if we make the constructor private, only objects of the class could call the constructor, but then how could a first object of the class be created? { I.e., The only way to create an object of the class is to already have an object of the class (chicken and egg problem) • The trick is to have a private constructor but a static method that creates instances of the class { Since we're dealing with a singleton, the static method will include code that insures that only a single instance of the class will be created • Sample code: public class Singleton { private static Singleton uniqueInstance; private Singleton() { } public static Singleton getInstance() { if (uniqueInstance == null) uniqueInstance = new Singleton(); return uniqueInstance; } ... } • This implementation is referred to as lazy creation { The instance is only created when needed • The first time getInstance() is called, uniqueInstance is null, so a new instance is created { On subsequent calls, an instance already exists, so that instance is returned 3 Singleton Pattern: Problems and Solutions • Suppose there are multiple threads accessing the getInstance() method { If the following sequencing occurs Thread 1 Thread 2 1 getInstance() 2 getInstance() 3 if (uniqueInstance == null) 4 if (uniqueInstance == null) 5 uniqueInstance = new Singleton() 6 uniqueInstance = new Singleton() 7 return uniqueInstance 8 return uniqueInstance we'll get 2 instances: at lines 5 and 6 { The code if (uniqueInstance == null) uniqueInstance = new Singleton(); return uniqueInstance; represents a critical section ∗ Only one process should be able to access this chunk of code at a time • Solutions to the problem 1. Lock { A lock is a means of guaranteeing that a section of code can only be accessed by one process at a time { In Java, declare critical sections/methods of code as synchronized public static synchronized Singleton getInstance() { if (uniqueInstance == null) uniqueInstance = new Singleton(); return uniqueInstance; } ... } { Issues: ∗ Locking out other processes from the getInstance() method is only critical on the first call ∗ Synchronization adds a fair amount of overhead that really is war- ranted only once 4 Singleton Pattern: Problems and Solutions (2) 2. Eager creation { Create the instance as soon as the program is loaded public class Singleton { private static Singleton uniqueInstance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return uniqueInstance; } ... } { Issues: As discussed earlier 3. Double-checked locking { Perform 2 checks on uniqueInstance's value public class Singleton { private volatile static Singleton uniqueInstance; private Singleton() { } public static Singleton getInstance() { if (uniqueInstance == null) synchronized (Singleton.class) { if (uniqueInstance == null) uniqueInstance = new Singleton(); } return uniqueInstance; } ... } { volatile indicates that uniqueInstance may be accessed by unsynchro- nized threads ∗ This prevents certain optimizations form being used { synchronized (Singleton.class) locks the entire class for the block that follows { Issues: (a) Reduces the overhead encountered in the first approach (b) Only recommended for Java 5+ 5 Singleton Pattern: Singlton v Other Approaches 1. Static variables • Only recommended if class is self-contained and does not require complex initialization • Otherwise, can result in subtle bugs 2. Globals • Preclude lazy creation • Encourage poor programming practices 3. Class loaders • Allows multiple instances of a singleton - one for each class loader 6 Singleton Pattern: Other Issues 1. One class, one responsibility • Guiding principle of OOP is that every class should have a single responsi- bility • Singleton class has 2 (a) Manages its own instance (b) Performs its intended role in program • Could abstract out the role functionality 2. Subclassing • Cannot subclass class with private constructor • To subclass, would need to make constructor protected or public, defeating the purpose of the singleton • If do change accessibility of constructor, subclasses will all access the same instance of uniqueInstance, since it's static { Probably not what was intended by subclassing 7.