Singleton, a Java singleton pattern, and its seven implementations

What is singleton mode

sketch

Singleton Pattern is one of the simplest and most widely used design patterns in creative design patterns. Singleton Pattern belongs to creative pattern and provides an idea of object creation.

When using singleton mode, the target class is required to ensure that there is only one instance. For any access to the target class in the system, there is no need to instantiate the class separately, but only need to access the only object instance created by the target class.

characteristic

  • The target class that uses the singleton pattern has only one instance
  • A unique instance is created and managed by the target class itself
  • The target class provides external instance access methods

Application scenario

Consider using singleton mode in the following scenarios

  • Certain instances need to be created frequently and destroyed frequently after use
  • Instantiation of a class is time-consuming, resource intensive and frequently used
  • A class of instances frequently interact with databases or files
  • A class contains global status fields, or there should be only a single instance in business logic

Advantages and disadvantages

Advantages of using singleton mode:

  • Reduce the overhead, because there is only one instance in memory, reduce the memory overhead, and reduce the performance (time) overhead because there is no need to instantiate classes frequently.
  • Avoid multiple occupation of a resource. For example, when writing to some files, a singleton can ensure that there is only one write to the file at the same time.
  • The target class provides a unique instance access method, and instances can be accessed as shared resources.
  • In some scenarios, there should only be a single instance of a class logically. Using the single instance mode can help implement it.

Disadvantages of singleton mode:

  • Contrary to the opening and closing principle (that is, open to extension and closed to modification), you can only modify the code when you want to extend a class.
  • It is easy to violate the principle of single responsibility, that is, a function is written in one class, which is easy to become miscellaneous.

Seven implementations of singleton mode

Lazy single case

As the name suggests, the singleton mode implemented in this way is to create an instance only when the target class is to be used. The advantage of this lazy loading is that no instance will occupy memory space when the target class is not actually used.

Thread unsafe mode

This implementation method is thread unsafe. When the target class instance has not been created, if two threads access getInstance() at the same time, it is easy to generate multiple instances and destroy the uniqueness of the instance. It is not recommended.

public class Singleton {
    public static Singleton instance;
    // Use private constructor to avoid external instantiation and ensure singleton
    private Singleton(){}

    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

Thread safe mode

For the implementation of the former thread unsafe method, you can use the synchronized keyword to modify the method to ensure that the method can only be accessed by one thread at the same time. Although this method can ensure thread safety and implement the singleton at the same time, after the singleton is created, synchronized loses its role in this scenario (ensuring the uniqueness of the instance) and reduces the performance overhead

public class Singleton {
    public static Singleton instance;
    // Use private constructor to avoid external instantiation and ensure singleton
    private Singleton(){}
	//Use synchronized to ensure thread safety
    public synchronized static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

DCL double check lock

As mentioned earlier, in order to ensure thread safety, the lazy type introduces synchronized, which leads to an increase in performance overhead. Therefore, a single instance implementation of Double Check Lock is derived. The volatile keyword is used to ensure visibility and avoid problems caused by JVM instruction rearrangement. At the same time, synchronized is used to ensure that the instance creation process is unique, which ensures the thread safety of lazy loading, Compared with the synchronized modification method, the performance overhead is reduced.

public class DCLSingleton {
    // Note that instance must be modified by volatile keyword to ensure the visibility of instance variable and avoid destroying the uniqueness of single instance due to instruction rearrangement
    private static volatile DCLSingleton instance;
    
    private DCLSingleton() {}
    
    public static DCLSingleton getInstance() {
        if(instance == null) { //First, determine whether to instantiate
            //Use synchronized locking to ensure that instances are created uniquely
            synchronized (DCLSingleton.class) {
                if(instance == null) {
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}

Hungry Han style single case

The idea of hungry Chinese singleton is to use the static keyword to create a unique instance of the target class in the class loading stage, so as to ensure that the instance has been created before being accessed, ensure uniqueness and thread safety.

Static variable

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

Static code block

public class Singleton {

    private static Singleton instance;

    private Singleton(){}

    static{
        instance = new Singleton();    
    }
    
    public static Singleton getInstance(){
        return instance;
    }
}

Static inner class

When the external class is loaded, the static internal class will not be loaded together, but will be loaded and instantiated when the static internal class is actually used (getInstance is called). Therefore, using the static internal class to implement the singleton mode has the advantage of lazy loading, and the JVM can ensure thread safety during class loading.

public class SingletonStaticClass {

    private SingletonStaticClass(){}
    
    public static final SingletonStaticClass getInstance(){
        return SingletonInstance.instance;
    }

    private static class SingletonInstance{
        private static final SingletonStaticClass instance = new SingletonStaticClass();
    }
}

enumeration

The enumeration implementation singleton is the safest and easiest to write. The JVM will ensure that the enumeration class cannot be reflected and the constructor can only be called once, which completely solves the problem that other singleton modes are vulnerable to reflection, serialization and deserialization attacks (see below). It is recommended.

Enumeration class singleton

public enum EnumSingleton {
    INSTANCE;
    public void method() {
        System.out.println("doSomething");
    }
}

Existing class transformation

public class EnumSingletonEnhance {
    private EnumSingletonEnhance(){}

    public enum SingletonEnum {
        SINGLETON_ENUM;
        private EnumSingletonEnhance instance = null;
        private SingletonEnum(){
            instance = new EnumSingletonEnhance();
        }
        public EnumSingletonEnhance getinstance(){
            return instance;
        }
    }
    
}

Keywords: Java Design Pattern Singleton pattern

Added by Bluelove on Mon, 03 Jan 2022 18:13:45 +0200