Top 10 ways on how to implement Java singleton design pattern

0
170
singleton design pattern
singleton design pattern

Singleton design pattern is highly used design pattern and is equally controversial. Singleton Design Pattern is one of the twenty-three well-known GoF design patterns. I will publish series of posts where you will learn design patterns in detail and how to program them. In this article we will learn a few highly used implementations of this singleton design pattern.

This java singleton design pattern is basically to control the creation of a number of objects. Generally, we make constructor as private to make sure that the outside world cannot create object at all. This provides one static method which simply returns the object and it creates an object only when there is no creation before.

PS: I was inspired by Swapnil Vhotkar overview on Jira and was encouraged to publish my articles on this platform. If you like my article, please leave comments and I will personally read and respond.

Uses of Singleton design pattern

There are several places where it is advised to use Singleton design pattern, for example: Logging, Caching, Load-Balancer, Configuration, Communication (IO or remote to avoid poor performance) and DB Connection-pooling. One example of singleton is Runtime class.

Java Singleton Design Pattern
Java Singleton Design Pattern

Loopholes of singleton design pattern

Before writing the Singleton class, you must think if you really need one. You must keep the following demerits in mind:

  • They cause tightly coupled code
  • They are in control of their own life-cycle
  • You can mock or test them easily
  • You cannot extend them (open/closed principle)

Let us look at the class diagram for Singleton design pattern:

class diagram
class diagram

Now let us look at Singleton examples in java which are meant for single instance at any time per class-loader.

Eagar Loading

A vanilla way of implementation using eager loading mechanism, which is mostly getting used by the beginners. But in case of multi-threaded application, it would be a bad approach. You can see below singleton pattern java example.


public class MySingleton {

private static final MySingleton mySingleton = new MySingleton();

private MySingleton(){}

public static MySingleton getInstance(){

return mySingleton;

}

}

Lazy Loading

Singleton programming can also be done using a lazy loading mechanism. In the case of a multi-threaded application, it would be a bad approach.


class MySingleton {

private static MySingleton mySingleton;
private MySingleton(){}

public static MySingleton getInstance(){
if(null == mySingleton) {
mySingleton = new MySingleton();
}
return mySingleton;
}
}

Multi-threaded

A multi-threaded approach can avoid the race condition to ensure it won’t violate the philosophy of a singleton. But in the example below, making the whole method ‘synchronized’ is not a good approach, as we need to put the lock on the object creation statement only.


class MySingleton {

private static MySingleton mySingleton;
private MySingleton(){}

public synchronized static MySingleton getInstance(){
if(null == mySingleton) {
mySingleton = new MySingleton();
}
return mySingleton;
}
}

Threadsafe – Object Lock

The below multi-threaded way of implementation can avoid the race condition to ensure it won’t violate the philosophy of singleton and with the help of double-checked locking using object-level lock will achieve the same. This implementation guarantees thread safe; but the extra object is required to just keep a lock which is not a good practice. Another downside is that someone can take the advantage of using class-level lock as your lock is on a different object.


class MySingleton {

private static MySingleton mySingleton;
private static final Object lock = new Object();
private MySingleton(){}

public static MySingleton getInstance(){
if(null == mySingleton) {
synchronized(lock) {
if(null == mySingleton) {
mySingleton = new MySingleton();
}
}
}
return mySingleton;
}
}

Threadsafe – Class Lock

Another multi-threaded-based implementation (to avoid race conditions) can be achieved with the help of double-checked locking using a class-level lock. Here, marking the MySingleton object as volatile will ensure that changes made by one thread should be visible in another. This implementation guarantees thread safety.


class MySingleton {

private volatile static MySingleton mySingleton;
private MySingleton() {}

public static MySingleton getInstance() {
if (null == mySingleton) {
synchronized(MySingleton.class) {
if (null == mySingleton) {
mySingleton = new MySingleton();
}
}
}
return mySingleton;
}
}

Reflection

One of my favorite ways of implementation; here an intelligent constructor will stop singleton contract violation using reflection.


class MySingleton {

private volatile static MySingleton mySingleton;

//Reflection can't hack to create more than one object.
private MySingleton() throws Exception {
if (null == mySingleton) {
mySingleton = new MySingleton();
} else {
throw new Exception("It's a singleton class; don't expect more object to get produced");
}
}

public static MySingleton getInstance() throws Exception {
if (null == mySingleton) {
synchronized(MySingleton.class) {
if (null == mySingleton) {
mySingleton = new MySingleton();
}
}
}
return mySingleton;
}
}

Threadsafe – Lazy Loading

Here’s a very popular implementation using a static class, which brings the powers of lazy loading and thread safety. To implement Singleton with this approach, you should refer below code.


public class MySingleton {

private MySingleton() {}

private static class SingletonUisngInner {
private static MySingleton mySingleton = new MySingleton();
}

public static MySingleton getInstance() {
return SingletonUisngInner.mySingleton;
}
}

Cloneble Interface

In some circumstances, if your singleton class is inheriting Cloneable interface properties, then your singleton class needs extra care to prevent the singleton design contract. Your singleton class should override the clone method and explicitly throws the CloneNotSupportedException.


class ClonedClass implements Cloneable {

//Some logic
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}

public class MySingleton extends ClonedClass {
private MySingleton() {}

private static class SingletonUisngInner {
private static MySingleton mySingleton = new MySingleton();
}

public static OneMore getInstance() {
return singletonUisngInner.mySingleton;
}

public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
}

Enum

Another, and our final, very popular and smart way of implementing singletons is using enum, which takes care of all the issues we’ve so far.


public enum EnumSingleton{
INSTANCE;
}

Across JVM

Sometimes, people talk about singletons across multiple JVMs, so let’s touch on that. Singleton means only one object and we know very well that the object life-cycle is managed by the JVM, so one shared object across multiple JVMs is not possible.

But if you need to, you can probably create the object in one JVM and distribute it as a serialized object that could be used by other JVMs (but keep in mind that you are deserializing it, so keep in mind that anything static or marked as transient will not be achieved, and somewhere, the singleton contract is breaking away). You can also try using RMI server objects as singletons to fit your needs.

Enjoy!

LEAVE A REPLY

Please enter your comment!
Please enter your name here