Singleton mode of 23 design modes

Posted by coolfool on Sun, 23 Jan 2022 16:31:50 +0100

Singleton mode
What is singleton mode?

​ Ensure a class has only one instance, and provide a global point of access to it. (ensure that there is only one instance of a class, and instantiate it and provide the instance to the whole system.)

Advantages of singleton mode
  • Since the singleton mode has only one instance in memory, the memory expenditure is reduced, especially when an object needs to be created and destroyed frequently, and the performance cannot be optimized during creation or destruction, the advantages of the singleton mode are very obvious.
  • Since the singleton mode generates only one instance, the system performance overhead is reduced. When more resources are required for the generation of an object, such as reading the configuration and generating other dependent objects, a singleton object can be generated directly at the application startup, Then use the permanent resident memory to solve it (when using singleton mode in Java EE, you need to pay attention to the JVM garbage collection mechanism).
  • Singleton mode can avoid multiple occupation of resources. For example, when a file is written, because only one instance exists in memory, it can avoid simultaneous write operations on the same resource file.
  • Singleton mode can set global access points in the system to optimize and share resource access. For example, a singleton class can be designed to be responsible for the mapping of all data tables.
Disadvantages of singleton mode
  • Singleton mode generally has no interface, so it is difficult to expand. If you want to expand, there is basically no second way to realize it except modifying the code. Why can't singleton mode add interfaces? Because the interface has no meaning to the singleton pattern, it requires "self instantiation", and providing a single instance, interface or abstract class cannot be instantiated. Of course, in special cases, the singleton mode can realize the interface and be inherited, which needs to be judged according to the environment in the system development.
  • Singleton mode is bad for testing. In the parallel development environment, if the singleton mode is not completed, it can not be tested. There is no interface, and you can not use mock to virtual an object.
  • The single case mode conflicts with the principle of single responsibility. A class should only implement one logic, regardless of whether it is singleton or not. Whether it needs singleton depends on the environment. Singleton mode integrates "Singleton" and business logic into one class.
Singleton usage scenario
  • Environment requiring generation of unique serial number;
  • A shared access point or shared data is required in the whole project, such as a counter on a Web page. You can use the singleton mode to maintain the counter value and ensure thread safety without recording each refresh in the database;
  • Creating an object consumes too many resources, such as accessing IO, database and other resources;
  • The environment that needs to define a large number of static constants and static methods (such as tool classes) can adopt the singleton mode (of course, it can also be declared as static directly).
Type of singleton mode
  • Hungry man style: as the name suggests, it is to complete initialization when the class is loaded. It seems that it is really "hungry". The advantage of this mode is that the thread is safe, but the disadvantage is that it may cause a waste of memory and will be created when the instance does not need to be used

    public class HungrySingleton {
        /**
         * 1. The constructor is privatized, and external instances can be created
         */
        private HungrySingleton() {
            System.out.println(Thread.currentThread().getName() + "Instance created");
        }
    
        /**
         * 2.Create an object instance inside this class
         */
        private final static HungrySingleton instance;
        static {
            instance = new HungrySingleton();
        }
    
        /**
         * 3. Provide a public static method to return the instance object
         */
        public static HungrySingleton getInstance() {
            return instance;
        }
    
        public static void main(String[] args) {
            //The test found the same instance twice
            HungrySingleton instance1 = HungrySingleton.getInstance();
            HungrySingleton instance2 = HungrySingleton.getInstance();
            System.out.println(instance1 == instance2);
        }
    }
    
  • Lazy style: from the name, we know that the lazy mode is very "lazy". We will create instances only when we use it. Before creation, it will judge whether the object reference is empty. If multiple threads enter the judgment at the same time, multiple instance objects will be generated, which is not in line with the idea of singleton. This mode can ensure that the thread is a singleton only under single thread, so it is not recommended to use it.

    public class LazySingleton {
        private static LazySingleton instance;
        private LazySingleton() {
            System.out.println(Thread.currentThread().getName() + "Instance created");
        }
    
        /**
         * Provide a static public method
         * Although synchronized can be added here to ensure thread synchronization, it will have a great impact on efficiency
         */
        public static LazySingleton getInstance() {
            if (instance == null) {
                instance = new LazySingleton();
            }
            return instance;
        }
    
        public static void main(String[] args) {
            //Multithreading environment
            for (int i = 0; i < 10; i++) {
                new Thread(() -> LazySingleton.getInstance(), String.valueOf(i)).start();
            }
        }
    }
    
  • Double check: in fact, double check is an optimization of lazy mode, which ensures thread safety, and does not need to perform synchronization every time, which improves efficiency. Recommended for use in development.

    public class DoubleCheckSingleton {
        private static volatile DoubleCheckSingleton instance;
        private DoubleCheckSingleton() {
            System.out.println(Thread.currentThread().getName() + "Instance created");
        }
    
        /**
         * Provide a static public method, add double check code, solve thread safety problems, and solve lazy loading problems at the same time
         * At the same time, it ensures the efficiency and is recommended to use
         *
         * @return
         */
        public static DoubleCheckSingleton getInstance() {
            //Two checks are added in the modification, and the synchronization is not performed every time, which improves the efficiency and ensures the efficiency at the same time
            if (instance == null) {
                synchronized (DoubleCheckSingleton.class) {
                    if (instance == null) {
                        instance = new DoubleCheckSingleton();
                    }
                }
            }
            return instance;
        }
        public static void main(String[] args) {
            //Multithreading environment
            for (int i = 0; i < 10; i++) {
                new Thread(() -> DoubleCheckSingleton.getInstance(), String.valueOf(i)).start();
            }
        }
    }
    
  • Static inner class: compared with lazy and starving modes, static inner class mode (commonly known as Holder) is a singleton implementation recommended by many people, because it achieves the purpose of delayed loading with less code than lazy mode. As the name suggests, this mode uses a private static inner class to store singletons of external classes. This static inner class is generally called Holder. This method ensures both thread safety and lazy loading. It is strongly recommended.

    public class StaticInnerSingleton {
        private StaticInnerSingleton() {
            //Add the judgment of instance==null to the private constructor of the external class to prevent reflection from invading the external class
            if (SingletonHolder.instance != null) {
                throw new IllegalStateException();
            }
        }
    
        private static class SingletonHolder {
            private static StaticInnerSingleton instance = new StaticInnerSingleton();
        }
    
        public static StaticInnerSingleton getInstance() {
            return SingletonHolder.instance;
        }
    
    }
    
  • Enumeration: with jdk1 5 to implement the singleton mode. It can not only avoid the problem of multi-threaded synchronization, but also prevent deserialization and re creation of new objects. This approach is advocated by Josh Bloch, author of Effective Java

    public enum EnumSingleton {
        /**
         * example
         */
        INSTANCE;
        public void execute() {
            System.out.println("execute");
        }
    
        public static void main(String[] args) {
            EnumSingleton instance1 = EnumSingleton.INSTANCE;
            EnumSingleton instance2 = EnumSingleton.INSTANCE;
            System.out.println(instance1 == instance2);
            instance1.execute();
            //Multithreading environment
             for (int i = 0; i < 10; i++) {
               new Thread(() -> DoubleCheckSingleton.getInstance(), String.valueOf(i)).start();
             }
        }
    }
    

Topics: Java Design Pattern