Single case design mode

Posted by ScottCFR on Sat, 19 Feb 2022 06:26:59 +0100

Singleton Pattern is a creation pattern, which provides the best way to create objects.

This pattern involves a single class that is responsible for creating its own objects while ensuring that only a single object is created. This class provides a way to access its unique object, which can be accessed directly without instantiating the object of this class.

An example of single mode structure

The main roles of singleton mode are as follows:

  • Singleton class. Only one instance of a class can be created
  • Access class. Using singleton classes

Second, the implementation of singleton mode

There are two types of singleton design patterns:

Hungry Chinese style: class loading will cause the single instance object to be created

Lazy: class loading does not cause the single instance object to be created, but only when the object is used for the first time

  1. Hungry Han style - mode 1 (static variable mode)

    /**
     * Hungry Han style
     *      Static variables create objects of classes
     */
    public class Singleton {
        //Private construction method
        private Singleton() {}
    
        //Create an object of this class at the member location
        private static Singleton instance = new Singleton();
    
        //Provide a static method to get the object
        public static Singleton getInstance() {
            return instance;
        }
    }
    

    explain:

    This method declares the static variable of Singleton type at the member position, and creates the object instance of Singleton class. The instance object is created as the class loads. If the object is large enough and has not been used, it will cause a waste of memory.

  2. Hungry Chinese style - mode 2 (static code block mode)

    /**
     * Hungry Han style
     *      Create this type of object in a static code block
     */
    public class Singleton {
    
        //Private construction method
        private Singleton() {}
    
        //Create an object of this class at the member location
        private static Singleton instance;
    
        static {
            instance = new Singleton();
        }
    
        //Provide a static method to get the object
        public static Singleton getInstance() {
            return instance;
        }
    }
    

    explain:

    In this way, a static variable of Singleton type is declared at the member position, and the object is created in the static code block and for the loading of the class. Therefore, it is basically the same as hungry man mode 1. Of course, this mode also has the problem of memory waste.

  3. Lazy - mode 1 (thread unsafe)

    /**
     * Lazy style
     *  Thread unsafe
     */
    public class Singleton {
        //Private construction method
        private Singleton() {}
    
        //Create an object of this class at the member location
        private static Singleton instance;
    
        //Provide a static method to get the object
        public static Singleton getInstance() {
    
            if(instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    explain:

    In this way, a static variable of Singleton type is declared at the member position, and the assignment operation of the object is not carried out. When was the assignment? The Singleton class object is created only when the getInstance() method is called to obtain the Singleton class object, which realizes the effect of lazy loading. However, if it is a multithreaded environment, thread safety problems will occur.

  4. Lazy - mode 2 (thread safe)

    /**
     * Lazy style
     *  Thread safety
     */
    public class Singleton {
        //Private construction method
        private Singleton() {}
    
        //Create an object of this class at the member location
        private static Singleton instance;
    
        //Provide a static method to get the object
        public static synchronized Singleton getInstance() {
    
            if(instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    explain:

    This method also realizes the effect of lazy loading and solves the problem of thread safety. However, the synchronized keyword is added to the getInstance() method, resulting in a particularly low execution effect of the method. From the above code, we can see that the thread safety problem only occurs when initializing instance. Once the initialization is completed, it does not exist.

  5. Lazy - mode 3 (double check lock)

    Let's talk about locking in lazy mode. For getInstance() method, most operations are read operations, which are thread safe. Therefore, we don't have to make each thread hold a lock to call this method. We need to adjust the timing of locking. This also produces a new implementation mode: double check lock mode

    /**
     * Double check mode
     */
    public class Singleton { 
    
        //Private construction method
        private Singleton() {}
    
        private static Singleton instance;
    
       //Provide a static method to get the object
        public static Singleton getInstance() {
    		//For the first time, if instance is not null, it will not enter the lock grabbing stage and return the instance directly
            if(instance == null) {
                synchronized (Singleton.class) {
                    //After grabbing the lock, judge whether it is null again
                    if(instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

    Double check lock mode is a very good singleton implementation mode, which solves the problems of singleton, performance and thread safety. The above double check lock mode looks perfect, but it is actually a problem. In the case of multithreading, null pointer problems may occur. The reason for the problem is that the JVM will optimize and reorder instructions when instantiating objects.

    To solve the problem of null pointer exception caused by double check lock mode, you only need to use volatile keyword, which can ensure visibility and order.

    /**
     * Double check mode
     */
    public class Singleton {
    
        //Private construction method
        private Singleton() {}
    
        private static volatile Singleton instance;
    
       //Provide a static method to get the object
        public static Singleton getInstance() {
    		//In the first judgment, if instance is not null, it will not enter the lock grabbing stage and return to the actual state directly
            if(instance == null) {
                synchronized (Singleton.class) {
                    //After grabbing the lock, judge whether it is empty again
                    if(instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

    explain:

    The double check lock mode after adding volatile keyword is a better singleton implementation mode, which can ensure thread safety and no performance problems in the case of multithreading.

  6. Lazy - mode 4 (static inner class mode)

    In the static internal class singleton mode, the instance is created by the internal class. Because the JVM will not load the static internal class in the process of loading the external class, it will be loaded only when the properties / methods of the internal class are called, and its static properties will be initialized. Because static attributes are modified by static, they are guaranteed to be instantiated only once, and the instantiation order is strictly guaranteed.

    /**
     * Static internal class mode
     */
    public class Singleton {
    
        //Private construction method
        private Singleton() {}
    
        private static class SingletonHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
    
        //Provide a static method to get the object
        public static Singleton getInstance() {
            return SingletonHolder.INSTANCE;
        }
    }
    

    explain:

    The INSTANCE will not be initialized when the Singleton class is loaded for the first time. Only getInstance is called for the first time, and the virtual machine loads SingletonHolder

    And initialize INSTANCE, which can not only ensure thread safety, but also ensure the uniqueness of Singleton class.

    explain:

    Static inner class singleton mode is an excellent singleton mode, which is commonly used in open source projects. Without any lock, it ensures the safety of multithreading without any performance impact and waste of space.

  7. Enumeration mode

    Enumeration class implementation singleton mode is the highly recommended singleton implementation mode, because the enumeration type is thread safe and can only be loaded once. Using this feature of enumeration to implement the singleton mode, the writing method of enumeration is very simple, and the enumeration type is the only singleton implementation mode that will not be destroyed.

    /**
     * Enumeration mode
     */
    public enum Singleton {
        INSTANCE;
    }
    

    explain:

    The enumeration method belongs to the evil Chinese style.

Topics: Design Pattern Singleton pattern