An introduction to the singleton design pattern and common implementations
Photo by Safar Safarov on Unsplash
Introduction
The singleton design pattern restricts the instantiation of a class to a single instance. This is done in order to provide coordinated access to a certain resource, throughout an entire software system. Through this design pattern, the singleton class ensures that it?s only instantiated once, and can provide easy access to the single instance.
Use Case
Common use-cases for the singleton design pattern include factories, builders, and objects that hold program state.
Singletons are sometimes considered to be an alternative to global variables or static classes.
Compared to global variables, singletons have the following benefits:
- Singleton instance fields don?t take up space in the global namespace
- Singletons may be lazily initialized (to be discussed further)
Primarily due to the fact that a singleton holds an instantiated object, whereas static classes do not, singletons have the following advantages over static classes:
- Singletons can implement interfaces
- Singletons can be passed as parameters
- Singletons can have their instances swapped out (such as for testing purposes)
- Singletons can be handled polymorphically, so there may exist multiple implementations
Implementation
Let?s look at the implementation details of a basic singleton class in Java. Singletons are typically implemented with a private constructor method, and a public static method to return the instance of the singleton ? stored in a private static final variable.
There are two types of singleton implementations: eager and lazy initialization. They differ in the way they initialize the singleton instance. We must also consider thread-safety in each of them.
Eager initialization
In this version, the singleton instance is created when the singleton variable is initialized, not when it is first used. Since the singleton has no use at this point in the program?s execution, it may consume system resources unnecessarily. If the singleton instance is costly to compute or takes up a lot of resources, this may reduce system performance. This version is, however, thread-safe.
Lazy initialization
In this version, the singleton instance is created when the static getInstance method is first called. This ensures that the singleton instance only consumes system resources when it is absolutely necessary.
Thread-safe Lazy Initialization
The version of lazy initialization shown above is not thread-safe. The singleton instance may be created multiple times, in a program with multiple threads, all using the Singleton class simultaneously. If the singleton object is very costly to create, this may consume a lot of available system resources. Additionally, this can result in threads receiving a partially-created singleton object.
The below implementation is thread-safe through the use of synchronization. The instance variable is now also declared as volatile , which ensures that all threads have an updated view of the singleton instance.