Skip to content

Singleton

Singleton is a design pattern that describes how to create an object that can be constructed in a given application at most once.

Types

Singletons can be divided into two main groups:

  • lazy - an object instance is only created when the application wants to use it for the first time.
  • eager - an object instance is created on startup of the application.

There are two ways to create an eager singleton in Java:

  • eager with the usage of class
  • eager with the usage of enum.

Both of the above types are suitable for multi-threaded applications.

There are also two ways to create a lazy singleton in Java:

  • lazy, which is not suitable for use in multi-threaded applications.
  • lazy double checked , which can be used in multi-threaded applications.

That gives us a total of four ways to create singletons.

Pros and cons

The singleton is a controversial pattern because it breaks the Single Responsibility rule. Beyond that, the same instance is used globally in the application, it can create many dependencies between objects. Finally, the code with a singleton may become unreadable and difficult to fix in the case of application errors. However, the singleton also has its advantages. It can save the memory used by the application, and time if creating the object is time-consuming.

Despite the disadvantages, the singleton is a very important and often used pattern. It is often used in applications based on the Spring framework.

Construction

Because the singleton requires one class instance , hence we must:

  • block the possibility of creating an object by using the operator new, i.e. we must declare the constructor as private.
  • the field of the object in which the singleton will be stored should be "detached" from the class instance, i.e. the reference to the singleton will be kept in the static field.
  • we have to add the possibility to access such an instance (or create it for a lazy singleton). Because the singleton field is static, the method that gets the reference value should be static too.

singleton

Eager singletons

Class-based singleton

In order to create an eager singleton based on a class, we must remember all construction points. The example below shows how to define such a singleton and then how to use it.

package pl.sdacademy;

public class SimpleCounter {

  // static field in which we keep the singleton reference
  // it is an eager singleton so we create an instance by assigning it to the field
  private static final SimpleCounter INSTANCE = new SimpleCounter();

  // getter for singleton reference
  public static SimpleCounter getInstance() {
    return INSTANCE;
  }

  // hidden constructor
  private SimpleCounter() {}

  private int currentCount = 0;

  public int getCurrentCount() {
    return currentCount;
  }

  public void increment() {
    currentCount++;
  }
}
public class SimpleCounterUsage {
  public static void main(String[] args) {
    SimpleCounter simpleCounterA = SimpleCounter.getInstance();
    SimpleCounter simpleCounterB = SimpleCounter.getInstance();
    System.out.println(simpleCounterA == simpleCounterB); // true -> both references point to the same object

    simpleCounterA.increment();
    simpleCounterB.increment();
    System.out.println(simpleCounterA.getCurrentCount()); // 2
  }
}

ATTENTION: The singleton reference field and corresponding getter can have any name, but the instance name is most often used .

Singleton based on enum

We can also use enum to create a eager singleton. For this purpose, such an object should have one value available. The value of the enum object is initialized at the JVM startup and we can be sure that it will happen exactly once. The enum object can have only private constructors, so we don't need to define it. To get the reference value, we do not need to create an additional getter, because the enum allows access to the value using dot notation. So, the simplest enum-based eager singleton might look like this:

public enum SimpleSingletonExample {
  INSTANCE;
}

Previous example using an enum instead of a class would look like this:

public enum SimpleCounter {
  INSTANCE;

  private int currentCount = 0;

  public int getCurrentCount() {
    return currentCount;
  }

  public void increment() {
    currentCount++;
  }
}
package pl.sdacademy;

public class SimpleCounterUsage {
  public static void main(String[] args) {
    SimpleCounter simpleCounterA = SimpleCounter.INSTANCE;
    SimpleCounter simpleCounterB = SimpleCounter.INSTANCE;
    System.out.println(simpleCounterA == simpleCounterB); // also true

    simpleCounterA.increment();
    simpleCounterB.increment();
    System.out.println(simpleCounterA.getCurrentCount()); // 2
  }
}

Lazy singletons

The lazy singleton, unlike eager singlets, must be created when the reference for such a singleton is taken for the first time, e.g.:

package pl.sdacademy;

import java.util.ArrayList;
import java.util.List;

public class CommonStorage {
  private static CommonStorage instance;

  public static CommonStorage getInstance() {
    if (instance == null) { // (1)
      instance = new CommonStorage(); // (2)
    }
    return instance;
  }

  private List<Integer> values = new ArrayList<>();

  private CommonStorage() {
  }

  public void addValue(final int value) {
    values.add(value);
  }

  public List<Integer> getValues() {
    return values;
  }
}
package pl.sdacademy;

public class CommonStorageSampleUsage {
  public static void main(String[] args) {
    CommonStorage commonStorageA = CommonStorage.getInstance(); // the instance is CREATED at this time
    CommonStorage commonStorageB = CommonStorage.getInstance(); // second access to previously created instance
    System.out.println(commonStorageA == commonStorageB); // true

    commonStorageA.addValue(1);
    commonStorageB.addValue(2);
    System.out.println(commonStorageA.getValues().size()); // list size is 2
  }
}

In the example above, consider the lines of code labeled as (1) and (2).. Suppose two threads (let's call them threads A and B) at the same time called the getInstance() method on the CommonStorage singleton.

The following situation is possible:

  • thread A executes the (1) line, enters the if block, but does not yet execute the (2) code line, i.e. the instance field is stillnull.
  • thread B executes the line (1), enters the if block
  • thread A executes the (2) line, creates the first instance of the singleton
  • thread B executes line (2) and creates a second instance of the singleton, which is contrary to the assumptions of this design pattern.

The problem described above can be eliminated by using the so-called double checked singleton, which uses a synchronized block. Besides, this singleton checks twice that the instance field is equal to null - the first time without the synchronized block, the second time in the synchronized block. The purpose of double checking is to eliminate the need to create an expensive synchronized block when the instance has already been initialized. The definition of such a singleton might look like this:

package pl.sdacademy;

import java.util.ArrayList;
import java.util.List;

public class CommonStorage {
  private static CommonStorage instance;

  public static CommonStorage getInstance() {
    if (instance == null) { // (1)
      synchronized (CommonStorage.class) {
        if (instance == null) { // (2)
          instance = new CommonStorage(); 
        }
      }
    }
    return instance;
  }

  private List<Integer> values = new ArrayList<>();

  private CommonStorage() {
  }

  public void addValue(final int value) {
    values.add(value);
  }

  public List<Integer> getValues() {
    return values;
  }
}

In the above example, if two threads simultaneously call the getInstance method, we guarantee that the reference will be initialized exactly once.