Singleton mode of design mode

Keywords: Java Design Pattern Singleton pattern

Singleton mode

Baidu Encyclopedia: Singleton mode is a common software design mode of creation type. The class created by the method of singleton mode has only one instance in the current process (if necessary, it may belong to singleton in a thread, for example, only the same instance is used in the thread context)

summary

Personal understanding: in the singleton mode, only one instance is allowed. The advantage of this sub mode is to avoid memory waste caused by the existence of multiple instances. For example, if I create an instance of A, whether B operates the instance of A or C operates the instance of A, the instance of A is still unique.

Characteristics of singleton mode

  • A singleton pattern class has only one instance object
  • The singleton pattern class must be created by the singleton class itself
  • The singleton pattern class provides a global access point for accessing the singleton

Advantages of singleton mode

  • Singleton mode can ensure that there is only one instance in the memory, which reduces the memory overhead
  • Multiple occupation of resources can be avoided
  • Set global access points in singleton mode to optimize and share resource access

Disadvantages of singleton mode

  • The singleton mode generally has no interface and is difficult to expand. If extension is required, there is no second way except to modify the original code, which violates the opening and closing principle.
  • In concurrent testing, singleton is not conducive to code debugging. During debugging, if the code in the singleton is not executed, a new object cannot be simulated.
  • The function code of singleton mode is usually written in a class. If the function design is unreasonable, it is easy to be ignored by the single responsibility principle.

The structure and implementation of singleton mode

Singleton pattern is one of the simplest design patterns. Generally, the constructors of ordinary classes are public, and external classes can generate multiple instances through "new constructor()". However, if the constructor of a class is made private, the external class cannot call the constructor and cannot generate multiple instances. At this time, the class itself must define a static private instance and provide a static public function to create or obtain the static private instance.

Structure of singleton mode

Main roles:

  • Singleton class: a class that contains an instance and can create the instance itself.
  • Access class: a class that uses a singleton.

The structure diagram is as follows:

Implementation of singleton mode

The Singleton pattern usually has two implementations. (there are also three kinds on Baidu. I only mention two kinds here, lazy type single case and hungry type single case)

The first type: hungry Han style

The second: lazy single case

  • Lazy - thread unsafe:
    • Single thread 🆗
    • Multithreading can cause problems

The most basic implementation method is the thread context singleton, which does not need to be shared with all threads, nor does it need to add locks such as synchronize to improve performance.

/**
 * @author:pier 2021/11/11
 **/
/**Lazy mode*/
public class LazyMantest {
    private LazyMantest(){
        //Print whether the thread outputs normally
        System.out.println(Thread.currentThread().getName()+"OK");
    }
     private static LazyMantest lazyMantest;
     public static LazyMantest getInstance(){
         //If it is empty, it is created on demand
         if(lazyMantest==null) {
             lazyMantest=new LazyMantest();
         }
         return lazyMantest;
     }
    public static void main(String[] args) {
        //Generate 10 threads
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                LazyMantest.getInstance();
            }).start();
        }
    }
}

The results of three operations are as follows:

From the above results, we can see that the running results generated by us are not safe and will be caused by missing values, and the running results are different every time.

  • Lazy double lock mode: in the above code, only the following methods have been modified

On the lazy basis, the synchronize keyword and volatile keyword are used to ensure that there is no competition between threads and multiple instances are generated during the first creation. Only the first creation is synchronized, and the performance is relatively high

    /**volatile Is a lightweight synchronization mechanism provided by Java.
     * Java Language contains two inherent synchronization mechanisms:
     *  Synchronize blocks (or methods) and volatile variables,
     *  Compared with synchronized (which is often called heavyweight lock),
     *  volatile More lightweight because it does not cause thread context switching and scheduling.
     *  However, volatile variables have poor synchronization (sometimes it is simpler and less expensive),
     *  And its use is more error prone.
     */
private volatile static LazyMantest lazyMantest;
public static LazyMantest getInstance(){
         /**Lazy DCL with dual detection lock mode*/
         if(lazyMantest==null){
             synchronized (LazyMantest.class){
                 if(lazyMantest==null) {
                     lazyMantest=new LazyMantest();
                     /**
                     * Not atomic operation
                     * 1,Allocate memory space
                     * 2,Execute the construction method to initialize the object
                     * 3 Point this object to this space
                     *
                     * Expectation 123
                     * But it may go to 132 A
                     *     B*///At this time, lazMan has not completed the construction
                 }
             }
         }
         return lazyMantest;//When we come down again, we may not have gone out
     }

In the above code, the two if judgments let our singleton mode always command to load a thread to realize the singleton operation. However, such code is not atomic operation (see code comments for details); Therefore, we need to add a keyword volatile when the object is loaded

So we need to add another sentence

  • Lazy - static inner class
/**Static inner class*/
public class Holder {
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }
    public static class InnerClass{
        private static final Holder HOLDER = new Holder();
    }
}

You think it's over? Is the test above safe? Forget, is there another cattle B operation called reflection?

  • Use reflection change? The strongest dual test mode at present
		LazyMantest instance = LazyMantest.getInstance();
        Constructor<LazyMantest> declaredConstructor = LazyMantest.class.getDeclaredConstructor(null);
        LazyMantest instance1 = declaredConstructor.newInstance();
        System.out.println(instance);
        System.out.println(instance1);
        System.out.println(instance.equals(instance1));

  • Solve reflection problems

    • Thinking, add a reflection exception and a judgment value to use the decompiled value. Refer to the enumeration mode!

    What is enum enum?

    Itself is a class.

package com.Design pattern.Singleton;

import java.lang.reflect.Constructor;

/**
 * @author:pier 2021/11/12
 **/
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}
class Test{
    public static void main(String[] args) throws Exception{
        EnumSingle instance = EnumSingle.INSTANCE;
        /*NoSuchMethodException: com.Design pattern. Singleton. Enumsingle. < init > ()
        * This exception is thrown here. This exception means that there is no empty parameter structure in our enumeration
        * So through a series of operations and decompilations, we found two parameters, String type and int type
         */
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        //IllegalArgumentException: Cannot reflectively create enum objects
        //Do not use reflection to change enumeration classes
        declaredConstructor.setAccessible(true);
        EnumSingle instance1 = declaredConstructor.newInstance();

        System.out.println(instance);
        System.out.println(instance1);
    }
}

Singleton mode instance

The lazy singleton model is used to simulate the generation of the current American presidential object.

Analysis: in each term of office, there is only one president of the United States, so this example is suitable for implementation in singleton mode. Figure 2 shows the structure diagram of implementation in lazy singleton mode.

Code example

package com.Design pattern.Singleton;

/**
 * @author:pier 2021/11/12
 **/
public class SingletonLazy {
    public static void main(String[] args) {
        President zt1 = President.getInstance();
        zt1.getName();    //Output the president's name
        President zt2 = President.getInstance();
        zt2.getName();    //Output the president's name
        if (zt1 == zt2) {
            System.out.println("They are the same person!");
        } else {
            System.out.println("They are not the same person!");
        }
    }
}
class President {
    /** Ensure that instance is synchronized in all threads */
    private static volatile President instance = null;
    /**private Avoid classes being instantiated externally*/
    private President() {
        System.out.println("Produce a president!");
    }
    public static synchronized President getInstance() {
        //Add synchronization to getInstance method
        if (instance == null) {
            instance = new President();
        } else {
            System.out.println("There is already a president, can not produce a new president!");
        }
        return instance;
    }
    public void getName() {
        System.out.println("I am the president of the United States: trump.");
    }
}

Operation results

Posted by rekha on Fri, 12 Nov 2021 03:23:52 -0800