In this story I will talk about the Singleton design pattern. What it is? Why is important? When to use it? and I will provide different implementations in Java language.

What is Singleton?

If you search in a dictionary the word Singleton (https://www.dictionary.com/browse/singleton) you will see that the first definition is: a person or thing occurring singly, especially an individual set apart from others. One other definition in Mathematics is: a set which contains exactly one element. This is the definition that we will talk about today.

In software the singleton patterns refers to the restriction of the instantiation of a class to a single instance per classloader.

Why we need Singleton pattern?

Why or when is useful to have only one instance of a class?

To answer this question first we let’s see what will happen with our application if we don’t use only one instance. Let’s take the classical example with the database connection objects. If we create a new object for each database connection, we will have a lot of database connection objects.

If we have a REST API that makes a query to the database, and let’s say that we have a traffic of 100 req/s, then we would will create 100 objects/s. If an database object is let’s say for the sake of the argument 100kb, then 100 req equals 10mb. We are filling the memory of our application with 10mb per second.

Not to mention that if you have an application with high traffic like 1000 req/s the things go crazier, you are filling the memory with 100mb per second, you will soon run out of memory. We are talking here only for the database connection object, in a real scenario you will have multiple objects in this situation.

At some point the garbage collection will kick in, this comes with a cost, the performance of your application will decrease.

Of course my example is exaggerated, i just wanted to point out the importance of not creating more objects that is needed.

The purpose of the singleton class is to control object creation, limiting the number of objects to only one. The singleton allows only one entry point to create the new instance of a specific class.

Another example for a singleton is when you have an app that uses an database of any kind and you need a connection pool. The purpose of such a pool is to reuse DB connection, so you definitely want all clients to use the same DB connection pool. Thus, having it as a singleton is the correct design.

The Singleton anti-pattern

The singleton was invented for the reasons that I described above, but it doesn’t mean that is suitable for every context, and that you should write every object as a singleton. More than that is considered as an anti-pattern.

Main reasons that is considered an anti-pattern are:

  • Singletons are difficult to test and write unit tests on them
  • The singleton pattern is especially difficult to implement correctly when taking multi-threading into account.
  • Singletons are bad when used with multi-threading because with just a single object, the options of threading are limited.

If you want to read more in detail about this topic you can check these two links:

  • https://dzone.com/articles/singleton-anti-pattern
  • https://www.michaelsafyan.com/tech/design/patterns/singleton

This doesn’t mean that you shouldn’t use singleton at all, it is nevertheless an extremely common and popular pattern. You should use it carefully, only when you really need it.

Implementing the Singleton pattern in Java

There are multiple ways of implementing the singleton in Java, I will present them below and tell for each of them what disadvantages they have and how to fix them.

Eager initialization

This is the most simple method to make a singleton, in this case we are creating the instance on class loading. By making the constructor private we are not allowing other classes to instantiate the Singleton class, we have added the getInstance() method, that is used for returning the object created when the class is first loaded.

public class EagerInitializationSingleton {

    private static volatile EagerInitializationSingleton  singleton= new EagerInitializationSingleton();

    private EagerInitializationSingleton(){} //private constructor

    private static EagerInitializationSingleton getInstance(){
        return singleton;
    }
}

One of the drawbacks of this approach is that we are creating an instance even though client application may not be using it. This may be a problem in some cases and can create memory leak. To solve this we will need what is called a Lazy initialization singleton.

Lazy initialization

Lazy initialization refers to the idea of creating a resource only when you need it. In our case we don’t create the singleton if the getInstance() method is never called. On the first call of the getInstance() we create the singleton object and the next time this method is called, no new object will be created but will be returned the existing object.

public class LazyInitializationSingleton {

    private static volatile LazyInitializationSingleton singleton;

    private LazyInitializationSingleton() {} //private constructor

    private static LazyInitializationSingleton getInstance() {

        if (null == singleton) { // if no instance is created we create one
            singleton =  new LazyInitializationSingleton();
        }

        return singleton;
    }
}

This is much better then the first approach but we can still improve, think about using this in a multi threading environment, if two or more threads access the getInstance() method in the same time, more than one instance will be created. We need to make the singleton thread safe.

Thread safe Singleton

There are two ways to make the singleton thread safe.

  • Adding synchronized keyword to method signature
  • Double check locking method

Using synchronized on getInstance();

As we have made the getInstance() synchronized, all other threads will need to wait for the first thread to finish executing this method.

There are some cons of using this approach:

  • Slow performance because of locking overhead.
  • Unnecessary synchronization that is not required once the instance variable is initialized.
public class ThreadSafeSingleton {

    private static volatile ThreadSafeSingleton singleton;

    private ThreadSafeSingleton() {} //private constructor

    private synchronized static ThreadSafeSingleton getInstance() { // we synchronize the method

        if (null == singleton) { // if no instance is created we create one
            singleton =  new ThreadSafeSingleton();
        }

        return singleton;
    }
}

Using double check locking on getInstance();

To solve the performance issues, with the above code, instead of synchronizing the whole method, we only synchronize the code that initializes the singleton.

We need to check again inside the synchronized block if the singleton has been initialized by another tread or not, to avoid initializing more than one time the singleton.

It is possible that both threads enter the same time in the first if statement. The first one will enter the synchronized block, the second one will wait after the first thread, and then the second thread will also enter the synchronized block, if the second if statement wasn’t there the singleton would have been initialized two times.

public class DoubleCheckLockingSingleton {

    private static volatile DoubleCheckLockingSingleton singleton;

    private DoubleCheckLockingSingleton() {} //private constructor

    private static DoubleCheckLockingSingleton getInstance() {

        if (null == singleton) { // if no instance is created we create one

            synchronized (DoubleCheckLockingSingleton.class) { // we use synchronized block to make it thread safe
                if (null == singleton) { // we check again  to be sure that no other thread initialized  this variable
                    singleton = new DoubleCheckLockingSingleton();
                }
            }
        }

        return singleton;
    }
}

Reflection safe Singleton

You may think that we are done, but not yet. The above singleton is good but is not reflection safe. It is possible to use java reflection API to inject an instance of the singleton.

The solution for preventing this is simple, we need to check inside the constructor that the instance is not initialized. If we find that we already have an instance, it means that somebody is using reflection, so we just throw an run time exception.

public class ReflectionSafeSingleton {

    private static volatile ReflectionSafeSingleton singleton;

    //private constructor
    private ReflectionSafeSingleton() { 
        //Protection for Reflection api
        if (singleton != null){
            throw new RuntimeException("Please use getInstance() method for getting an instance,");
        }
    }

    private static ReflectionSafeSingleton getInstance() {

        if (null == singleton) { // if no instance is created we create one

            synchronized (ReflectionSafeSingleton.class) { // we use synchronized block to make it thread safe
                if (null == singleton) { // we check again  to be sure that no other thread initialized  this variable
                    singleton = new ReflectionSafeSingleton();
                }
            }
        }

        return singleton;
    }
}

Serializing the singleton

There is only one thing left and our singleton will be perfect. In some special cases we will need to serialize and deserialize the singleton. The problem is that during the deserialization process it will create a new instance of the singleton.

To fix this we have to provide the implementation of readResolve() method.The readResolve() replaces the object read from the stream.

public class SerializingTheSingleton implements Serializable {

    private static volatile SerializingTheSingleton singleton;

    //private constructor
    private SerializingTheSingleton() {
        //Protection for Reflection api
        if (singleton != null){
            throw new RuntimeException("Please use getInstance() method for getting an instance,");
        }
    }

    private static SerializingTheSingleton getInstance() {

        if (null == singleton) { // if no instance is created we create one

            synchronized (SerializingTheSingleton.class) { // we use synchronized block to make it thread safe
                if (null == singleton) { // we check again  to be sure that no other thread initialized  this variable
                    singleton = new SerializingTheSingleton();
                }
            }
        }

        return singleton;
    }

    //Ensures that no new instance is created during the serialization and deserialization
    protected SerializingTheSingleton readResolve() {
        return getInstance();
    }
}

Note

If you read with attention the code provided in this article, you will see that I have used the volatile keyword.

private static volatile SerializingTheSingleton singleton;

Why? Because reader threads are not doing any locking and until writer thread comes out of synchronized block, memory will not be synchronized and value of singleton will not be updated in main memory. With Volatile keyword in Java, this is handled by Java himself and such updates will be visible by all reader threads.

Singleton as Enum

In the last coupe of years a new approach has been popular that is to create singleton class by using enums. Since enums fields are compiled time constants, they are instances of their enum type. And, they’re constructed when the enum type is referenced for the first time.

Joshua Bloch has explained this approach in his Effective Java Reloaded talk at Google I/O 2008: https://sites.google.com/site/io/effective-java-reloaded

public enum Foo {
   INSTANCE;
}

Conclusion

Singleton can be tricky to get it right, and if you are not careful it can easily become an anti-pattern. The easiest way and most simplest way to implement it is using Enums. Also the synchronized singleton, with reflection protection and serialization safe is good to use and should be fine for most applications.

Hopefully this article was useful and you learned something.

Leave a Reply

Your email address will not be published. Required fields are marked *

Close Search Window