The Singleton Pattern
The Singleton Pattern
Contextual Forces
Motivation
Allow for only one instance of a given class to ever exist. Provide a mechanism to obtain this instance that any entity can access.
Encapsulation
- The construction of the instance.
- The fact that the number of instances is constrained.
- The mechanism by which the instantiation is made threadsafe, if it is.
- Optionally, the destruction of the instance.
Procedural Analog
Since the Singleton is concerned with instantiation, which is not an issue in procedural software, a direct analog is difficult to determine. However, the Singleton serves a similar role to a global variable or function, and in fact can be used to eliminate the need for globals. Therefore, wherever you might consider the need for a global, you might consider the possibility of using a Singleton.
Non-Software Analog
A radio station broadcasts at a specific frequency, and within a given context (a "market"). No other station can broadcast at this same frequency.
Any radio in the same context can receive the program currently being broadcast by tuning to the frequency assigned to the station in question. No matter how many radios are tuned to this station, there is and can be only one broadcast at a given point in time on it.
The number of radios tuned to the station is unconstrained, and thus the station is not designed for any particular number of listeners. The radios in the market can tune in the station by themselves, without any interaction with a controlling entity.
Radio stations and radios interact through an established protocol, which is part of their design.
Implementation Forces
Example
public class Singleton{
private static Singleton _instance;
private Singleton(){}
public static Singleton getInstance(){
if(_instance == null) {
_instance = new Singleton();
}
return _instance;
}
}
Questions, concerns, credibility checks
The example as shown is not threadsafe. If the Singleton is not re-entrant, and will be deployed in a multi-threaded environment, then a threadsafety option will have to be chosen. See Options in Implementation for various ways of accomplishing this.
Will the Singleton have read-write state? This should be avoided if possible as this is essentially the creation of a global variable in the system. Global variables are to be avoided because they make coupling very difficult to control.
Options in implementation
Threadsafety: The Singleton is typically instantiated lazily; that is, the instance is not created until it is needed, and perhaps never (if no other entity ever calls getInstance()). However, this introduces a threadsafety problem if it is possible for one thread to be engaged in the creation of the instance while another is checking for null. In this worst-case scenario, it would be possible for two instances to be created. The first thread would have its own private instance; the second thread would have the instance that all other entities will now get. If this is a critical issue, then there are other implementations that should be considered.
1. Deterministic Instantiation
If the "lazy" aspect of the Singleton is not important in a given circumstance, then the instance can be created as the class loads. This is threadsafe, as it uses the class loader, which must be threadsafe:
public class Singleton {
private static Singleton _instance = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return _instance;
}
}
This is possibly the simplest solution, and is preferred where lazy instantiation is not necessary. An alternative implementation of this same solution is to use a static constructor, if the language/platform provides one. In either case, the load is atomic and deterministic.
2. Using a synchronization semaphore
Most languages have a way of locking access to a method while a thread is running it. If you require lazy instantiation, you can use a "lock" or "synchronize" or similar mechanism to ensure threadsafety
public class Singleton {
private static Singleton _instance;
private Singleton(){}
//obtain lock
public static Singleton getInstance() {
if(_instance == null) {
_instance = new Singleton();
}
return _instance;
}
//release lock
}
The problem, potentially, is performance. Obtaining the lock is typically a fairly costly action, in terms of speed, and yet the lock is really only needed on the first call. Once the instance is created, the check for null will never pass. We pay the locking price on every call, however.
If performance is not critical, this is a simple way to achieve threadsafety and lazy instantiation.
3. Double-Checked, Locking Singleton
A common suggestion for obtaining both high-performance and laziness in a threadsafe Singleton is to use the following implementation, typically called a Double-Checked, Locking Singleton:
public class Singleton {
private static Singleton _instance;
private Singleton(){}
public static Singleton getInstance() {
if(_instance == null) { // once the instance is
// created, we will bypass
// the lock
//obtain lock here
if(_instance == null) { // a thread which waited
// for the lock will now
// check to see if the
// instance is still
// null.
_instance = new Singleton();
}
//release lock here
}
return _instance;
}
}
This double check ensures that a thread, which was waiting for the lock, will check again once it obtains it, in the off chance (worst-case scenario) that the previous thread created the instance while it was waiting. Once the instance is created, the first check never passes and so the lock is never taken, improving performance.
The potential problem here has to do with compiler optimization and memory management, and the issues are complex and contentious. One example, however, is this: the compiler may "notice" that there are two identical null checks on _instance, and that there is no code between them that could potentially change the state of the variable. Therefore, a compiler may "feel free" to optimize out one of the two calls, which destroys our threadsafety without our knowledge (unless we analyze the generated bytecode or machine language).
In some languages/platforms this may or may not be a concern, but it is certainly controversial at this point:
- Java discussion: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
- .Net discussion: http://blogs.msdn.com/cbrumme/archive/2003/05/17/51445.aspx
4. Nested Class
Another option for high-performance, lazy threadsafety, is to use a nested class. Here again, we are simply leaning on the threadsafety of the class loader, but the nested class (if it loads only when referenced) makes the Singleton lazy as well:
public class Singleton {
private Singleton(){}
public static Singleton getInstance() {
return Nested.instance; // causes the nested class to
// load, on first reference
}
private class Nested { // will not load until a member
// is referenced
public static Singleton instance;
static {
instance = new Singleton();
}
}
}
There are specific implementations in different languages and on different platforms (in .Net, for instance, the nested class must be marked BeforeFieldInit to make sure it does not load when Singleton loads), but the concept is the same.
Refactoring Issue
The Singleton is easily refactored from a simple encapsulated constructor:
public class User {
private user(){}
public static User getInstance() {
return new User();
}
}
This, as a recommended general practice, makes it easy to refactor a simple class into a Singleton without changing the entities that consume its services.
Encapsulated Destruction
In systems that do not manage memory for you (aka "unmanaged code") you may want to encapsulate the potential destruction of the instance as well:
public class Singleton {
private static Singleton _instance;
private Singleton(){}
public static Singleton getInstance() {
if(_instance == null) {
_instance = new Singleton();
}
return _instance;
}
public static void returnInstance(Singelton anInstance){
//Take whatever action is needed
}
}
All client entities would be coded to call getInstance() to obtain the instance, and returnInstance() when they were done with it.
In a typical Singleton, no action would be needed in returnInstance(), but this would allow for instance counting and the cleanup of memory if this is desired. This would, essentially, introduce a simple form of memory management to an unmanaged environment.
As mentioned in “Refactoring Issue” above, this is also a best-practice for encapsulating the destruction of entities in general.
Consequent Forces
Testing issues
The unit test for the Singleton would primarily be concerned with its functional aspects, which are not covered here as they will vary from case to case. However, the test can assert that two or more calls to the getInstance() method returns the same instance, using AssertSame or a similar call (depending on the testing framework used):
public void TestGetInstance() {
Singleton 1stInstance = Singleton.getInstance();
Singleton 2ndInstance = Singleton.getInstance();
AssertSame(1stInstance, 2ndInstance);
}
Whether this is necessary to test will vary from project to project. If this test is in place, it can be used to test-drive a change in Singleton for load-balancing (see Cost/Benefit).
Cost-Benefit (gain-loss)
- A limited-use resource can be protected from erroneous multiple use without complicating the consuming client entities.
- Singleton can easily scale to two, three, or more instances for load-balancing. Since the cardinality issue is encapsulated, this can be freely changed if this is deemed appropriate.
- If the Singleton gains statefullness, care must be taken to avoid creating a global.