In this article, we will go through all the scenarios of the Singleton pattern to make it crystal clear. From the definition, it seems to be a very simple design pattern but when it comes to implementation, it creates a lot of implementation concerns. Also, the implementation of Java Singleton pattern has always been a controversial topic among developers. Here, we will learn about Singleton Design Pattern in Java with all Scenarios, different ways to implement Singleton design pattern and some of the best practices for its usage.
Sometimes, we get the requirement for some classes to have exactly one instance. There are many occasions when we need only one instance of the Object and if we instantiate more than one, we’ll run into all sorts of problems like incorrect program behavior, overuse of resources, or inconsistent results. In this article, we will learn about ‘Singleton Design Pattern in Java with all Scenarios’ in detail and its related concepts.
Table of Contents (Click on links below to navigate)
- 1 Why & When to use the Singleton Design Pattern?
- 2 What is a Singleton Design Pattern?
- 3 How to implement a Singleton Design Pattern in general ?
- 4 Thread-Safe Singleton (Eager Initialization)
- 5 Singleton Violation on Using Reflection
- 6 Singleton Violation on Object Cloning
- 7 Singleton Violation on Serialization/Deserialization
- 8 Singleton in Multithreaded Environment
- 9 Holder Class (Bill Pugh Method)
- 10 Enum Singleton
- 11 Summary of Singleton Design Pattern with all Scenarios
- 12 Links to Other Design Patterns
Why & When to use the Singleton Design Pattern?
The Singleton Pattern puts restriction on the instantiation of a class and ensures that only one instance of the class exists in the java virtual machine. The singleton class must provide a global access point to get the instance of the class. Singleton pattern is generally useful when the object that is created once and shared across different threads/applications. If in your solution some object has only one instance and you want to model that in your design then you should use singleton pattern.
Some of the usage of the Singleton pattern are in thread pool, logging, caching, driver objects etc. We can even see the usage of Singleton design pattern in core java classes also. For example, java.lang.Runtime, java.awt.Desktop etc.
What is a Singleton Design Pattern?
There are only two points in the definition of a singleton design pattern,
1) There should be only one instance allowed for a class and 2) We should allow global point of access to that single instance.
How to implement a Singleton Design Pattern in general ?
In order to implement a Singleton pattern, we have different scenarios, but each of them has the following common approach.
- Private constructor to restrict instantiation of the class from other classes.
- Private static variable of the same class that is the only instance of the class.
- Public static method that returns the instance of the class, this is the global access point for outer world to get the instance of the singleton class.
Furthermore, we will discuss different Scenarios of Singleton pattern implementation and design concerns with the implementation.
Now, let’s discuss Singleton Design Pattern in Java with all Scenarios thoroughly.
Thread-Safe Singleton (Eager Initialization)
In eager initialization, an instance is created at the time of class loading. We will not have any thread safety issue if we choose to go with eager initialization. If your application always creates and uses an instance of the Singleton or the overhead of creation and runtime aspects of the Singleton are not burdensome, you may want to create your Singleton eagerly, like this:
Using this approach, we rely on the JVM to create the unique instance of the Singleton when the class is loaded. The JVM guarantees that the instance will be created before any thread accesses the static uniqueInstance variable. Now here is one catch! We have to think about thread safety. In that case, we go with lazy initialization.
*No worries! We will learn about lazy initialization as well in a while.
There are some examples where Singleton pattern violation situation can be found in a thread-safe environment. However, we will use the hash code comparison technique to verify the equality of two objects. If two objects are equal, they MUST have the same hash code.
Singleton Violation on Using Reflection
Using reflection, we can set the private constructor to become accessible at runtime as shown in the example below.
From the above output it is clear that using reflection singleton violation can happen.
How to Fix Violation
Throw Runtime Exception if someone tries to make instance, in case one instance already exists. Below code will go into the private constructor of the Singleton class accordingly.
Output After fix
Singleton Violation on Object Cloning
If we try to make an instance by cloning it, the generated hash code of cloned copy doesn’t match with the actual object so it also violates the Singleton principle.
How to Fix Violation
Throw CloneNotSupportedException from the clone () method if someone tries to make another instance of it. Add below code in clone() method of above SingletonR class.
Output After fix
Singleton Violation on Serialization/Deserialization
When we serialize an object and deserialize it again there are different hash code values generated as shown in the example below. Therefore, our Singleton principle breaks in case of object serialization/deserialization also.
How to Fix Violation
Implement a new readResolve () method in the Singleton class as shown below.
Output After fix
Singleton in Multithreaded Environment
Singleton will work properly in multi-threaded environment only if eager instantiation has been done because in this case instance creation will happen at the time of class loading only. But for Lazy instantiation we will have to take care of multiple things. If we want to delay the instantiation because of cost, we will have to go with lazy.
Lazy vs Eager Initialization:
Lazy initialization will be beneficial when we want to delay the initialization until it is not needed. On the other hand, if we use eager initialization and if initialization fails, there is no chance to get the instance further while in lazy initialization we may get it in second chance. In Lazy initialization we will not get instance until we call the getInstance method while in eager initialization, it creates instance at the time of class loading itself.
Following code demonstrates the behavior of Singleton instance, when two threads are getting executed by comparing their hash code values. Be careful while running the following code as it will work only in Java 8 and later versions. Moreover, we have used Method Reference in the code.
As shown by the output of the program, It is noticeable that in multithreaded environment sometimes Singleton principle works while sometimes it violates as multiple threads are trying to create instance. Therefore, we need to synchronize the getInstance () method as shown below.
Synchronizing block of code
After applying synchronize keyword in the getInstance () method the program may execute properly without any issue, but in Java instead of synchronizing whole method we can synchronize only the block of code which is affected while creating instance to escape the extra overhead as below.
Double Checked Locking
Now from the above code we have narrowed down the scope of synchronization for performance reasons. Notice that if a thread at line # 2 notices that instance is null and then it gets the lock of object at line # 3. At the same time if another thread already has the lock it will create the instance. So to make sure no other thread has already acquired the lock we will apply one more check after acquiring the lock as shown below. This technique is called Double Checked Locking. With double-checked locking, we first check to see if an instance is created, and if not, then we synchronize. This way, we only synchronize the first time through, just what we want.
In rare cases, double-checked locking also breaks the Principle of Singleton.
Java runtime publishes half initialized variable. To illustrate, suppose 2 threads thread1 & thread2 are entering into the code it goes through the line # 2 to line # 31 and created the instance. Furthermore, at the same time thread 2 enters and it knows that there is something in variable named as ‘instance’ (since it is at half initialized state) and it returns the same from line # 9. Therefore, Singleton principle breaks.
***Note : Double-checked locking doesn’t work in Java 1.4 or earlier! Unfortunately, in Java version 1.4 and earlier, many JVMs contain implementations of the volatile keyword that allow improper synchronization for double-checked locking. If you must use a JVM earlier than Java 5, consider other methods of implementing your Singleton. Although I guess nobody might be using JVM earlier than Java5 but I have mentioned this point for your knowledge purpose only. Also, you may take it as a point of improvement in Java 5 enhancements.
Using volatile keyword
To address above situation, use volatile keyword at the time of instance declaration. Value of volatile variable will be published only when the change completes. Change to write operation happens before read operation in volatile variable. In fact, all threads will see the same value of variable.
If performance is an issue in using getInstance() method, then this method of implementing the Singleton can drastically reduce the overhead.
Holder Class (Bill Pugh Method)
Bill Pugh came up with a different approach to create the Singleton class using an inner static helper class as below.
As shown from the output, this way of creating Singleton instance doesn’t violates Singleton Principle.
Joshua Bloch suggests the use of Enum to implement Singleton design pattern in Java as Java ensures that any enum value is instantiated only once in a Java program. Since Java Enum values are globally accessible, so is the singleton. The drawback is that the enum type is somewhat inflexible; for example, it does not allow lazy initialization.
Enum Singleton doesn’t violate principle of Singleton in any case described above.
Summary of Singleton Design Pattern with all Scenarios
Finally, we learned all about “Singleton Design Pattern in Java with all Scenarios” in a detailed manner. Specifically, we have touched almost all the ways to create Singleton object in this article. If we find any more ways, we will update the article in the future accordingly. Further, if you find any other way or any improvement on the whole article, please feel free to comment or send the email for our review & update.
If you are interested to learn other Design Patterns of GoF, kindly visit article on ‘Design Patterns in Java‘. Furthermore, If you want to know good books on Java Design Patterns, kindly visit the separate article ‘Java Design Patterns Book’.