Singleton Pattern — But don’t get too comfortable

Singleton pattern is definitely the easiest pattern of all the design patterns but trust me, many of us are doing wrong. So let’s find out more about this pattern.

We all know the motivation of using Singleton pattern. It’s a pattern which allows us to have only a single instance of the object throughout the life-cycle of the application. Why would that be important? Because first, you don’t want to create more than one heavy resource consuming objects when you really want only one. Second, sometimes, not only creating more than one instance is costly, it can take you to inconsistent state. For example, you wouldn’t want to have multiple database objects because changes in one, may make others inconsistent.

But now you might be thinking, if I just need one object and I want to access it from anywhere, why can’t I make it a global-variable? The answer is, yes, you can make it a global variable but what if that object creation requires heavy resource usage? Global variables might be created just when you application starts. You wouldn’t want too much of startup time for your Android app, right? These small things are easily noticeable in mobile world. To prevent this, we make use of lazy-loading, which means, create the object only when it is needed. We’ll see that in a second.

Now that we are clear why we want a singleton object, let’s see what should be the characteristics of a Singleton object.

  1. A singleton class shouldn’t allow other classes to instantiate it. That means, no class should be able to do new Singleton() at all. How do we prevent this? This one is simple. Just make the constructor of the singleton class private . This simple step makes sure that the object won’t be instantiated anywhere…except within the same class (private constructors can be called from the same class — This is true for private methods and fields too). Below is a simple example in Kotlin of a class with private constructors.
class Singleton private constructor()

2. A Singleton class should be accessible to the class: Even though we have restricted instantiation of the Singleton class, we can still have the access to that kind of object. This is by making a static method, which returns the Singleton object. In the below example, we have made a static method which checks if the instance is null. If so, it will instantiate a new object and assign it to the INSTANCE variable, which will be returned on subsequent calls to the getInstance method.

class Singleton private constructor(){

companion object {
private var INSTANCE: Singleton ? = null
fun
getInstance(): Singleton{
if(INSTANCE == null){
INSTANCE = Singleton()
}
return INSTANCE!!
}
}

}

Most of the people who implement singleton pattern get the above two things right. But the problem with the above code is that it may produce more than one instance of your Singleton class in a multi-threaded environment.

fun getInstance(): Singleton{
if(INSTANCE == null){
// <--- Imagine Thread 2 is here
            INSTANCE = Singleton() // <--- Imagine Thread 1 is here
        }
...

In the above code, both Thread 1 and Thread 2 will produce two distinct objects, defeating the purpose of a singleton class. This can be catastrophic to your application and might be difficult to debug also because as with other multi-threaded issues, they only happen in some circumstances.

So an easy and no-brainier fix would be to just execute the code within a synchronized lock. That would go like this:

fun  getInstance(): Singleton {
synchronized(this) {
if
(INSTANCE == null){
INSTANCE = Singleton()
}
return INSTANCE!!
}
}

The above code will solve the case of creation of multiple instances but it may pose a performance problem. This means, even after correctly instantiating a singleton object, the subsequent access to it will be synchronized, which is not necessary. There is no issue in reading an object in a multi-threaded environment. A good ball park metric would be to consider that any block under synchronized code-block, will slow down the execution of that block by a factor of 100. If you are “OK” with this cost and you don’t need to access that singleton object too often, you can stop here. But if you do need to access it multiple times, it would help if you optimize it.

To optimize it, let’s think what we actually need to optimize. We need to optimize only the object creation flow, not the reading of the object flow.

After the INSTANCE variable is created, synchronizing the flow is totally an unneeded overhead.

An easy fix here would be to create the object eagerly, than lazily. Here’s how would it look like:

class Singleton private constructor(){
companion object {
val INSTANCE = Singleton()
fun getInstance(): Singleton {
return INSTANCE
}
}
}

In the above case, JVM will make sure that any thread access the INSTANCE variable after it has been created. But then again, as we discussed, it make add up to your startup time where you are creating an object even thought you’d need to later or won’t need it ever.

So we will now take a look at “double-checked locking” which can help us overcome all the above issues which we’ve been talking about. In double-check locking, we’ll first see if the object is created or not, if not, then only we will apply synchronization.

companion object {
@Volatile private var INSTANCE: Singleton ? = null
fun
getInstance(): Singleton {
if(INSTANCE == null){
synchronized(this) {
INSTANCE
= Singleton()
}
}
return INSTANCE!!
}
}

As you can see, we are only applying synchronization during the object instantiation phase.

You might be thinking, what is that @Volatile annotation doing there. It’s the same as volatile keyword in Java. As you know, each thread has its copy of variables in its stack and all the accessible values are copied to its stack when the thread is created. Adding a volatile keyword is like an announcement that says “The value of this variable might change in some other thread” Adding volatile makes sure that the value of variable is refreshed. Otherwise, it may so happen that, that thread never updates its local cache.

As a final refactor, I’d like to make a small change and leverage Kotlin’s language construct. I really don’t like the screaming !! in the return statement.

companion object {
@Volatile private var INSTANCE: Singleton ? = null
fun
getInstance(): Singleton {
return INSTANCE?: synchronized(this){
Singleton().also {
INSTANCE
= it
}
}
}
}

So there you have it, the perfect way to may a Singleton. Your implementation may differ based on other considerations like your application not running in a multi-threaded environment or you are fine with the performance cost of putting synchronize keyword over the entire method.

Thanks for reading it. If you find it interesting, feel free to put a clap(or ten?) on it and share it. If you’d like to read more articles like this, you can follow me.

Here are few other articles that might interest you:


Singleton Pattern — But don’t get too comfortable was originally published in Hacker Noon on Medium, where people are continuing the conversation by highlighting and responding to this story.

You may also like...

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: