The Single Pattern is the simplest design pattern. Many times it's 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.
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
All about singleton pattern
1. 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 it’s 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
{
2. //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 don’t 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
3. 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)
{
4. //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. Let’s 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 it’s 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
5. 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() { }
*/
}
Let’s 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, it’s 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 it’s 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