Вы находитесь на странице: 1из 6

Articles from Jinal Desai .

NET
All About Singleton Pattern
2013-07-20 09:07:15 Jinal Desai

What is Singleton Pattern?


The Single Pattern is the simplest design pattern. Many times its important to have only one instance for a particular class, for example we need to provide configuration manager for all the configurations of our application. But, we need to restrict instantiation of configuration manager class to only one. So only one instance of configuration manager will serve all the requests for configuration of our application. To better understand the scenario following is pseudo code. if(configurationManagerInstance == null) configurationManagerInstance = new ConfigurationManager(); return configurationManagerInstance; Whenever any request arrives that demands configuration settings of our application, one instance of configuration manager will serve the request. If this is the first request it will going to instantiate the configuration manager instance otherwise it will return already instantiated configuration manager instance.

What Singleton Pattern Ensures?


Single Pattern ensures following things. It ensures that instance is created only once. It ensures that instance will never be null. It ensures that instance will be thread safe. Instance uses little memory. Provides lazy instantiation. Class is freezes for sub-classing and instantiation.

Implementation of a Singleton Pattern


The implementation of a singleton pattern involves a static member inside sealed public singleton class with a private constructor and a static public method GetInstance() which returns a reference to the static member (which holds only single instance of the class).

UML Diagram of Singleton Pattern The GetInstance() method is responsible for returning single instance of the class. If the static member is null it will instantiate the static member. The instantiation is done inside the GetInstance() method thus it needs to be thread safe. //sealed to make sure it's not inheritable public sealed class singleton {

//static member to ensure single placeholder for object throughout scope of the class private static singleton instance; //used for locking block of code private static readonly object locker = new object(); //private constructor to restrict direct instantiation of the class private singleton() { } public static singleton GetInstance() { //synchronization lock, only one thread will enter the block lock(locker) { if(instance == null) instance = new singleton(); } return instance; } /* public void OtherMethods() { } */ }

When Singleton Pattern is Preferred?


Singleton is preferred over global variables because it allows lazy allocation and instantiation. It also consumes resources only when required compare to global variables which in many languages always consume resources even if not required. The other scenario where singleton pattern preferred is in case of heavy objects or factories. If our object is large which takes reasonable amount of memory we normally wish to instantiate only one object. In case of implementing factories we also wish to provide single instance of the factory.

Why Singleton? Not Static Class


Because singleton dont require to use the static keyword everywhere, it preserves the conventional class approach. Because singleton can implement interfaces or can be derived from base classes if required. Because singletons can be used as parameters or objects.

Examples
Load Balancer Classes Configuration Classes Common Shared Resource Classes (printer class, scanner class, etc) Logger Classes (logging framework) Factories (session factories) DBMS classes

Implementation Scenarios
Multi-threading Implementation : In multi-threaded usage, multiple threads are simultaneously accessing singleton class. If the instantiation is not thread-safe then there might be the chances that two threads holding difference instance object for same singleton class, which will spoil the purpose of singleton pattern. So, while implementing singleton pattern it is common practice to make the object instantiation logic thread-safe. Previous example shows basic thread-safe implementation in csharp. Protected Constructor : Normally, the constructor of a singleton class implementation is private. But in

scenario where subclassing of a singleton class is needed we can keep constructor of a singleton class as private. There are drawbacks associated with this kind of implementation. Protected constructor means, the class can be instantiated through calling the constructor from another class within the same namespace. Solution to the problem is creating separate namespace for singleton class implementation purpose. For using derived class, we need to change GetInstance calls from Singleton.GetInstance() to NewSingleton.GetInstance(). Implementation Using Static Field (Early Instantiation) : In previous code example, singleton class is instantiated in synchronized block only when it calls GetInstance() for the first time. If we need to ensure instantiation when the class is loaded, not when it is called first time GetInstance() method (called Early Instantiated) then we can achieve this using static fields instantiated directly which declared. //sealed to make sure it's not inheritable public sealed class singleton { //static member to ensure single placeholder for object throughout scope of the class //Early Instantiation private static singleton instance = GetInstance(); //private constructor to restrict direct instantiation of the class private singleton() { } //no need of synchronized block to make it thread safe public static singleton GetInstance() { if(instance == null) instance = new singleton(); return instance; } /* public void OtherMethods() { } */ } Double Locking Implementation (Lazy Instantiation) : The idea behind the double is to avoid costly synchronization for every invocations of the GetInstance() method. The cost of synchronization differs from compiler to compiler and even as the compilers are evolved the cost of synchronization decreases. But, it still affects performance, it still costs performance. So application developers never want to waste processing time whenever possible. Double locking is implementation for the same. //sealed to make sure it's not inheritable public sealed class singleton { //static member to ensure single placeholder for object throughout scope of the class private static singleton instance; //used for locking block of code private static readonly object locker = new object(); //private constructor to restrict direct instantiation of the class private singleton() { } //synchronized block needed if instance is null public static singleton GetInstance() { if(instance == null) {

//synchronization lock, only one thread will enter the block lock(locker) { //double checking for nullability of instance object if(instance == null) instance = new singleton(); } } return instance; } /* public void OtherMethods() { } */ } Now lets dry run the double locking algorithm. Step 1. First thread enters the GetInstance() method. Step 2. First thread enters the synchronized block because instance is null. Step 3. Now second thread comes into the picture. Second thread enters the GetInstance() method. Step 4. Second thread tries to acquire the lock, but since the first thread holds the lock second thread waits. Step 5. First thread continues execution. The instance is null so it creates a singleton object and assigns to instance variable. Step 6. First thread exits the synchronization block and returns instance from the GetInstance() method. Step 7. Since synchronized block is no longer blocked by any other thread second thread continues execution. Step 8. Second thread acquires the synchronization lock and checks whether instance is null or not. Step 9. The instance is not null so rather than creating second instance created it will return the instance already created by first thread. (In single locking case second thread at Step 8. will found instance null and create another instance, which would break singleton pattern) So, this is how double locking implementation works. Out Of Order Writes Final scenario we discuss is out of order writes. It is basically dealt with the synchronization between object instantiation and calling of construction of singleton class to make instantiation complete. Lets again dry run double locking algorithm with some different aspect in mind. Step 1: First thread enters GetInstance() method and found instance is null. Step 2: First thread enters synchronization block and found again instance is null. Step 3: Since first thread founds instance null inside the synchronization block it will instantiate instance object, but still the constructor of Singleton class is not executed. Step 4: Second thread comes into the picture, and checks if instance is null. Step 5: Since instance is already instantiated by first thread second thread found instance not null. Step 6: So, second thread will return instance object instantiated by first thread but it is partially initialized Singleton object. Step 7: First thread continues execution by completing initialization of the Singleton object by executing its constructor and returns with instance object. So, in above dry run double locking mechanism breaks. The situation here we generated by above dry run is called out-of-order writes. To avoid out-of-order write problem we need to introduce local variable instance1 and second synchronization block. Sample code is shown below. //sealed to make sure it's not inheritable public sealed class singleton { //static member to ensure single placeholder for object throughout scope of the class

private static singleton instance; //used for locking first synchronization block of code private static readonly object locker = new object(); //used for locking second synchronization block of code private static readonly object locker1 = new object(); //private constructor to restrict direct instantiation of the class private singleton() { } //synchronized block needed if instance is null public static singleton GetInstance() { if(instance == null) { //first synchronization lock, only one thread will enter the block lock(locker) { Singleton instance1 = instance; if(instance1 == null) { //second level synchronization lock, only one thread will enter the block lock(locker1) { instance1 = new singleton(); } instance = instance1; } } } return instance; } /* public void OtherMethods() { } */ } Lets dry run above algorithm to check whether it addresses out-of-order write scenario or not. Step 1: First thread enters GetInstance() method. Step 2: The instance object is null, so it enters the first synchronization block and assign instance1 the value of instance which is null right now. Step 3: First thread now checks the value of instance1 which is null so enters second synchronization block and instantiate instance1 but constructor execution is still pending execution. Step 4: Now, second thread comes into the picture. Second thread enters into the GetInstance method and checks whether instance is null or not. Step 5: Since instance is still null (first thread still not assign initialized object to instance, its still reference initialized object to instance1), second thread tries to enter first synchronization block but since first thread is holding the lock second thread not able to enter first synchronization block. Step 6: First thread completes execution by assigning fully constructed object instance1 to instance and leaves both synchronization blocks and returns fully constructed instance object. Step 7: Second thread will get the access to first synchronization block and assigns instance object reference which is now not null to instance1. Step 8: Now, while checking not null of instance1, second thread will found its not null and already initialized. So, second thread will not enter the second synchronization block. Step 9: Second thread returns fully constructed instance object. There are some optimizations also possible to the above algorithm, but this is the basic algorithm which addresses both double-checked locking scenario as well as out-of-order writes scenario. References

OODesign Double-checked locking and the Singleton pattern

Вам также может понравиться