Let’s discuss how to create a singleton instance for a clustered (or) cloud environment. Before that few basics:
What
The singleton instance is a class that has exactly one instance at any given time.
Why
For a scenario where only one instance is required to solve a problem, singleton creates chaos if more than one instance is available. So, instead of solving the problem, it creates a new problem.
How
Types of instantiation:
- Lazy instantiation
- Instantiation is delayed until the caller method needs the singleton instance.
- Eager/early instantiation
- Instantiated while the class gets loaded.
There are multiple ways through which a singleton pattern’s objective can be achieved. The table below contains descriptions and links to the code.
# | Description/Steps | |
1 | 1. Make constructor private.2. Create self member variable as static.3. Add a public method to instantiate the member instance and return it.Check the GitHub link:https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionOne.javahttps://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/drivers/SingletonVersionOneDriver.java | |
2 | 1. Make constructor private.2. Create self member variable as static.3. Add a public method to instantiate the member instance.4. Synchronize at method scope to avoid multiple threads creating the instance.Check the GitHub link:https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionTwo.javahttps://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/drivers/SingletonVersionTwoDriver.java | |
3 | 1. Make constructor private.2. Create self member variable as static.3. Add a public method to instantiate the member instance.4. Synchronize only the method creation block to avoid multiple threads creating the instance.Check the GitHub link:https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionThree.javahttps://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/drivers/SingletonVersionThreeDriver.java | |
4 | 1. Make constructor private.2. Create self member variable as static.3. Add a public method to instantiate the member instance.4. Synchronize only the method creation block to avoid multiple threads creating the instance.5. Before instantiating inside the synchronized block, check if the instance is null and then instantiate it. This is also called Double LockingCheck the GitHub link:https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionFour.javahttps://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/drivers/SingletonVersionFourDriver.java | |
5 | 1. Make constructor private.2. Create self member variable as static and instantiate the member variable.3. Add a public method to return the created instance.Check the GitHub link:https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionFive.javahttps://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/drivers/SingletonVersionFiveDriver.java | |
6 | 1. Follow steps 1 to 3 from Version 5.2. Implement the Cloneable interface (Cloneable is a marker interface and does not contain any methods).3. Override clone() method from the Object class.4. Throw CloneNotSupportedException @Overrideprotected Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException();}Check the GitHub link:https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionSix.javaNote: Since the clone method is protected, the driver class is written inside the same package.https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionSixDriver.java | |
7 | 1. Follow steps 1 to 3 from Version 5.2. Implement the Cloneable interface (Cloneable is a marker interface and does not contain any methods).3. Override the clone() method from the Object class.4. What is the point in throwing CloneNotSupportedException when all we need is an instance. So just return the instantiated instance.Check the GitHub link:https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionSeven.javaNote: Since the clone method is protected, the driver class is written inside the same package.https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionSevenDriver.java | |
8 | 1. Follow steps 1 to 3 from Version 5.2. Implement the Serializable interface (Serializable is a marker interface and does not contain any methods).3. OverridereadResolve() method/** * This is a overridden method from Serializable interface.* Official documentation : https://docs.oracle.com/javase/7/docs/api/java/io/Serializable.html* Since ANY ACCESS SPECIFIERS can be used, I have used public*/public Object readResolve() { return singletonInstance;}Check the GitHub link:https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionEight.javahttps://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/drivers/SingletonVersionEightDriver.java | |
9 | 1. Follow steps 1 to 3 from Version 5.2. Mark the class as final.3. This would avoid any child classes getting inherited from singleton class.Check the GitHub link:https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionNine.javahttps://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/drivers/SingletonVersionNineDriver.java | |
10 | 1. Follow steps 1 to 3 from Version 5.2. Implement the Cloneable interface (Cloneable is a marker interface and does not contain any methods).3. Override clone() method from the Object class.4. Return the instantiated instance.5. Implement the Serializable interface (Serializable is a marker interface and does not contain any methods).6. Override the readResolve() method.7. Mark the class as final.8. This would avoid any child classes getting inherited from the singleton class.Check the GitHub link:https://github.com/sureshnnatarajan/core-java-design-patterns/blob/master/src/main/java/core/java/design/patterns/singleton/versions/SingletonVersionTen.java |
So far so good but is your singleton cloud-ready?
Singleton and a Cloud/Clustered Environment
The topics above are discussed per JVM. If more than one JVM is available in your application, which is typical in a clustered/cloud environment, what would be the best approach? Ask yourself the following questions:
- Will you go ahead with Singleton?
- Is there something else that would be better?
Well, it depends on the application. For now, let’s take a use case where we should use singleton but we are not sure if it is ready across the JVM (or) when a cloud provider is chosen.
Schedulers Example
Schedulers have their own set of advantages and disadvantages, but let’s not discuss those. Instead, let’s focus on how to make your singleton cloud-ready and/or make it work in a clustered environment.
Though there are various methods within a cloud environment, I preferred to choose a SQL-based database for registering a singleton instance and making use of the registered instance available for other containers.
Here is how:
1. Choose a SQL database.
2. I prefer MySQL (InnoDB engine — it has very good support for row-level locking).
3. Register a job or singleton instance (unique value) during the class load.
4. Since the value should adhere to the unique constraint of the database, no more than one record will get registered.
5. Any other containers registering with a unique value will get a unique constraint exception and has to deal with it.
6. When a container shut’s down, check if the registered job belongs to the container. If yes, then de-register before shutting down.
7. For every scheduled run, an additional check is needed to see if the job is registered by a container. If not, let the container register it.
Now What?
So, you have given an approach for using singleton on the cloud. However, what if the singleton design has to be used for a No-SQL database that is clustered?
Very good question. I will have to explore the options. I will come up with another article on how to deal singletons using a NoSQL database.
Stay tuned!