DEV Community

Ravi Yasas
Ravi Yasas

Posted on

Singleton Design pattern

What is the design pattern?

  • In simple words, design patterns are best practices in the software development field.
  • These design patterns are invented by well experienced Object-Oriented Software developers using their practice and experience.

Singleton design pattern

  • This design pattern can be used to control object creation of the software development life cycle.
  • If you use the Singleton design pattern, then you can have only one instance of that type.

How to create a Singleton object?

  • Create private constructor (Then no one can instantiate this class, it means others can't create objects)
  • Create the only one private static instance
  • Create a public method to invoke this instance

Types of singleton implementations

There are several ways to implement a singleton class.

  • Eager initialization
  • Static block initialization
  • Lazy initialization
  • Thread-safe initialization
  • Enum initialization

Eager initialization

  • In this method, the instance is created at the time of class loading.
  • The instance is created even the client is requesting or not.
public class Singleton{
    private static final Singleton instance = new Singleton();
    private Singleton() { }
    public static Singleton getInstance() {
       return instance;
    }
}
Enter fullscreen mode Exit fullscreen mode

Static block initialization

  • This is very same as Eager initialization.
  • The special thing is, we use the static block in getInstance() method to have exception handling.
public class Singleton{

    private static Singleton instance;

    private Singleton() { }

    static{
        try {
            instance = new Singleton(); 
        }catch (Exception e) {
            System.out.println("Error : " + e);
        }
    }

    public static Singleton getInstance(){
        return instance;
    }
}
Enter fullscreen mode Exit fullscreen mode

Lazy initialization

  • This is a good one in a single-threaded environment.
  • But in a multithreaded environment, we have to use thread-safe initialization.
public class Singleton{

    private static Singleton instance;

    private Singleton() { }

    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}
Enter fullscreen mode Exit fullscreen mode

The enum initialization

  • This is the most efficient way to implement a singleton.
  • Because it is very easy to write. Here is the way to implement it.
public enum EnumSingleton{
      instance;
}
Enter fullscreen mode Exit fullscreen mode
  • You can get the instance by calling EnumSingleton.instance

Thread-safe initialization

  • This is just very similar to Lazy initialization.
  • But here synchronize the method that can be used to get the instance in a multi-threaded environment.
public class Singleton {

    private volatile static Singleton instance;

    private Singleton() { }

    public static Singleton getInstance() { 
        if (instance == null) {
             synchronized (Singleton.class) {
                  if (instance == null) {
                        instance = new Singleton();
                  }
             }
        }
        return instance;
    }
}
Enter fullscreen mode Exit fullscreen mode

What is the double-checking on the Singleton design pattern?

  • Just think, you are using the code like below.
synchronized (ThreadSafe.class){
    if(instance ==null){
         instance = new ThreadSafe();
    }
}
Enter fullscreen mode Exit fullscreen mode
  • Here only one thread can acquire the lock of the object.
  • If many threads want to get the instance, all threads need to wait until one thread releases the lock of the object.
  • Since the synchronized block is using heavy CUP usage, it will consume more memory.
  • To resolve this, we can double-check the instance as follows.
if(instance ==null){
   synchronized (ThreadSafe.class){
       if(instance ==null){
           instance = new ThreadSafe();
       }
   }
}
Enter fullscreen mode Exit fullscreen mode
  • Now other threads will not reach the synchronized block. This will save CPU memory.

What is the use of Volatile?

  • The volatile keyword can be used with variables.
  • Volatile variables will not be cached in the cache memory.
  • It will be saved only in the main memory.
  • If one thread changed the value, all other threads can also see the changed value.

Which is the best one?

  • The enum singleton implementation is the best way because it is very easy to implement and use.
  • But this is available since Java 5.

When to use the Singleton design pattern?

  • To manage shared resources(Database connections, file manager)
  • Logging
  • Catching

Singleton in JDK

There are many singleton classes that can be found in JDK. I listed three of them.

  • In java.lang.Runtime which provides getRuntime() method.
  • In java.awt.Toolkit which provides getDefaultToolkit() method.
  • In java.awt.Desktop which provides getDesktop() method.

Discussion (0)