What is Singleton Design Pattern?
Singleton design pattern is a creational design pattern. It is used to maintain single instance throughout the application.
When to use Single Design pattern?
When our requirement requires a single instance throughout the application. Then we can take steps to implement this design pattern.
Where can we use it?
For Database connection, logging, caching purpose etc.
Below is the code which defined the Singleton design pattern. The below code is not thread safe.
- class singletonSample:
-
- __instance = None
-
- @classmethod
- def getInstance(cls):
- if not cls.__instance:
- cls.__instance = singletonSample()
- return cls.__instance
Below is the code to access singleton instance.To make sure we have single instance, I am accessing the class with Instance 10 times and matching with some refInstance object, along with printing object reference id i.e. identity of object
- from singletonSample import singletonSample
-
- instanceList = []
-
- for index in range(0, 10):
- instanceList.append(singletonSample.getInstance())
-
- refInstance = singletonSample.getInstance()
-
- print("refInstance object found at: ", id(refInstance))
-
- for instance in instanceList:
- if instance == refInstance:
- print("Same object found: ", id(instance))
- else:
- print("different object found:", id(instance))
Below is the output of above code
If we are using the threads in our application and we are expecting to maintain singleton instance even though the “class.Method” is accessed by threads, in these kind of cases we need thread safe singleton class.
Below is the thread safe code.
- import threading
-
- class threadSafeSingleton:
-
- __lockObj = threading.Lock()
- __instance = None
- __proposal: str
-
- @classmethod
- def getInstance(cls, proposal: str):
- with cls.__lockObj:
- if cls.__instance is None:
- cls.__proposal = proposal
- cls.__instance = threadSafeSingleton()
- return cls.__instance
-
- def printProposal(self):
- print(self.__proposal, "proposed to create instance")
In thread safe code we can see that we are using instance of threading.Lock() which will be used to lock current class to use for particular thread and unlock it once usage of thread is completed. If multiple threads try to access the instance of class, the first will acquire threading.Lock() and will proceed next, while the rest will wait until the first thread finishes its execution in that class. And again when second thread, third thread so on move one by one synchronously and as first thread already created instance, rest all threads will use same instance for further execution
Below is code where we are creating multiple instance in 4 different threads and while creating each instance we are passing thread name. as per above explanation class should maintain the proposal of first thread which is hit to thread safe singleton class,
- from threadSafeSingleton import threadSafeSingleton
- import threading
-
-
- def printfun(proposal: str):
- single = threadSafeSingleton.getInstance(proposal)
- single.printProposal()
-
-
- threadA = threading.Thread(target=printfun, args=['threadA'])
- threadB = threading.Thread(target=printfun, args=['threadB'])
- threadC = threading.Thread(target=printfun, args=['threadC'])
- threadD = threading.Thread(target=printfun, args=['threadD'])
-
- threadA.start()
- threadB.start()
- threadC.start()
- threadD.start()
Below is the output of Thread Safe Singleton accessing code. In the below output we can see that threadA hit the Thread Safe Singleton code and created instance and the remaining 3 threads; i.e. thread, threadC, threadD access the same instance created by threadA
Summary
In this article we learned how to create singleton instance and where to use it. And also we saw how to implement thread safe singleton instance.