Patrón singleton del patrón de diseño de Java (1)

Los funcionarios y los funcionarios pequeños siempre tienen problemas. Cuanto más alto, más alto, más alto es el precio, más no se puede comparar; cuanto más dinero no se puede comparar, más se tiene, mejor, es interminable; comer buena comida es todo sobre la comida, es bueno estar contento, renuncia al cuidado llega!

Aprendizaje de patrones de diseño, escribiré en un blog sobre 23 patrones de diseño en un futuro cercano , así que estad atentos ~

Introducción al modo singleton

El llamado patrón de diseño singleton consiste en tomar un cierto método para garantizar que en todo el sistema de software, solo pueda haber una instancia de objeto para una determinada clase , y que el objeto solo pueda proporcionar un método de instancia de objeto obtenido.

Enciclopedia de Baidu

8 categorías de patrones singleton

Se recomienda negrita

  • Chino hambriento (constante estática)
  • Chino hambriento (bloque de código estático)
  • Lazy (hilo inseguro)
  • Lazy (seguridad de subprocesos, método de sincronización, baja eficiencia)
  • Lazy (bloques de código sincronizados y seguros para subprocesos)
  • Doble verificación
  • Clase interna estática
  • enumerar

Chino hambriento (constante estática)

public class InstanceMode01 {
    
    
    //私有化构造器 外部不能  new
    private InstanceMode01(){
    
    }

    //类的内部实现创建对象
    private  static final InstanceMode01 instance = new InstanceMode01();

    //向外暴露一个静态方法
    public static InstanceMode01 getInstance(){
    
    
        return  instance;
    }
}
  • Ventajas: el código es simple, la instanciación se completa cuando se carga la clase y se evita la sincronización de subprocesos
  • Desventajas: debido a que se crea cuando se carga la clase, no se logra el efecto de carga diferida. Si no se usa la instancia actual, puede causar pérdida de memoria

Veamos si se implementa el singleton:

Juzgue si la dirección de memoria es la misma comparando hashCode () (la instancia es la misma):

InstanceMode01 instanceMode1 = InstanceMode01.getInstance();
InstanceMode01 instanceMode01 = InstanceMode01.getInstance();
Log.i("单利模式之饿汉静态方法01:",
             instanceMode1.hashCode() + "\t" + instanceMode01.hashCode());

效果图(1.1):

Sugerencia: se puede usar, pero desperdiciará memoria

Chino hambriento (bloque de código estático)

public class InstanceMode02 {
    
    
    //私有化构造器 外部不能  new
    private InstanceMode02(){
    
    }

    //类的内部实现创建对象
    private  static final InstanceMode02 instance;

    static {
    
    
        //静态代码块创建单利
        instance = new InstanceMode02();
    }

    //向外暴露一个静态方法
    public static InstanceMode02 getInstance(){
    
    
        return  instance;
    }
}

Las ventajas y desventajas son las mismas que las del estilo chino hambriento (constante estática ), porque la clase interna estática se ejecutará cuando se cargue la clase, y no hay diferencia con el estilo chino hambriento (constante estática ), pero la escritura es diferente.

Comparar hashCode:

效果图(1.2):

Sugerencia: se puede usar, pero desperdiciará memoria

Lazy (hilo inseguro)

public class InstanceMode03 {
    
    

    /**
     * 私有化构造器 外部不能  new
     */
    private InstanceMode03(){
    
    }

    private static  InstanceMode03 instance;

    //提供一个共有方法 使用到时才去创建intance
    public static InstanceMode03 getInstance(){
    
    
        if (instance == null) {
    
    
            instance = new InstanceMode03();
        }
        return instance;
    }
}
  • Ventajas: Juega un papel de carga diferida, ¡ningún desperdicio solo se puede usar en un solo hilo!

  • Desventajas: el subproceso no es seguro, no se puede utilizar el subproceso múltiple cuando está en subproceso múltiple

       假设有2条线程:
    
      线程A走到if(instance == null) 线程B也走到 if(instance == null)
    
      两个线程会同时走instance = new InstanceMode03();
      会创建2个实例,违背了单例模式的原则,所以在多线程的情况下不建议使用!
    
  public static InstanceMode03 getInstance(){
    
    
         if (instance == null) {
    
    
               instance = new InstanceMode03();
            }
             return instance;
     }

Comparar hashCode:

效果图(1.3):


No recomendado, ¡el multihilo es imprescindible en el desarrollo de proyectos!

Lazy (seguridad de subprocesos, método de sincronización, baja eficiencia)

public class InstanceMode04 {
    
    

    /**
     * 私有化构造器 外部不能  new
     */
    private InstanceMode04(){
    
    }

    private static InstanceMode04 instance;

    //提供一个共有方法 使用到时才去创建intance
    public static  synchronized InstanceMode04 getInstance(){
    
    
        if (instance == null) {
    
    
            instance = new InstanceMode04();
        }
        return instance;
    }
}
  • Ventajas: resuelva los problemas de seguridad de los hilos

  • Desventajas: eficiencia demasiado baja

     假设我现在要执行100次getInstance()
      那么这100次都要进行线程同步,而不是直接返回实例对象
     我想要的结果是,如果现在已经存在实例,那么直接return给我实例对象即可
     因为是线程同步,所以会排好队一个一个执行,效率低
    

Comparar HashCode:

效果图(1.4):


No recomendado: demasiado ineficiente

Lazy (bloques de código sincronizados y seguros para subprocesos)

public class InstanceMode05 {
    
    
    /**
     * 私有化构造器 外部不能  new
     */
    private InstanceMode05(){
    
    }

    private static InstanceMode05 instance;

    //提供一个共有方法 使用到时才去创建intance
    public static   InstanceMode05 getInstance(){
    
    
        if (instance == null) {
    
    
            synchronized (InstanceMode05.class){
    
    
                instance = new InstanceMode05();
            }
        }
        return instance;
    }
}

Desventajas: El
problema es el mismo que la inseguridad de subprocesos perezosos de InstanceMode03 (). El
problema de subprocesos no se resuelve, aunque se agrega el bloque de código.
En el caso de subprocesos múltiples, se crearán 2 instancias cuando 2 getInstance () sean obtenido al mismo tiempo.

¿Por qué la adición de sincronizado (InstanceMode05.class) () no resuelve el problema de subprocesos?

 public static   InstanceMode05 getInstance(){
    
    	//1
        if (instance == null) {
    
    	//2
            synchronized (InstanceMode05.class){
    
    	//3
                instance = new InstanceMode05();	//4
            }		//5
        }		//6
        return instance;
    }

Supuesto:
Ahora hay subproceso A y subproceso B. Cuando el subproceso A y el subproceso B se ejecutan sincrónicamente, cuando van al paso 2 al mismo tiempo, porque es sincrónico, por lo que si (instancia == nulo) () se puede ejecutar, ir al tercero Al dar un paso, cuando A ha ido primero, se ha creado la instancia, y ahora B todavía está esperando afuera, cuando B ha pasado el paso después de 3, se crearán 2 instancias, lo que viola la idea de singleton .

Comparar HashCode:

效果图(1.5):
Inserte la descripción de la imagen aquí
No recomendado, el hilo no es seguro

Doble verificación

public class InstanceMode06 {
    
    

    /**
     * 私有化构造器 外部不能  new
     */
    private InstanceMode06() {
    
    
    }

    /**
     *    volatile:防止JVM优化被禁止重排序的。
     *      重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。
     */
    private static volatile InstanceMode06 instance;
  
    /*1*/
    public static InstanceMode06 getInstance() {
    
    
        /*2*/
        if (instance == null) {
    
    
            /*3*/
            synchronized (InstanceMode06.class) {
    
    
                /*4*/
                if (instance == null) {
    
    
                    /*5*/
                    instance = new InstanceMode06();
                }
            }
        }
        return instance;
    }
}

palabra clave volátil:

   volatile解决问题:
 
    问题出在第5行:
  
在new创建变量的时候 instance = new InstanceMode06();
  
它并不是一个原子操作。事实上,它可以”抽象“为下面几条JVM指令:
            memory = allocate();  // 1:分配对象的内存空间
            ctorInstance(memory); // 2:初始化对象
            instance = memory;  // 3:设置instance指向刚分配的内存地址
      
操作2依赖于操作1,但是操作3并不依赖于操作2
所以JVM可以以“优化”为目的对它们进行重排序,经过重排序后如下:

             memory = allocate();  // 1:分配对象的内存空间
            instance = memory;  // 3:设置instance指向刚分配的内存地址
      		 // 注意,此时memory对象还没有被初始化!
            ctorInstance(memory); // 2:初始化对象
  
   这样做有什么区别呢?
   正常情况是先分配对象的内存空间,然后初始化,最后instance指向分配的内存地址
   现在有可能是先分配对象,然后直接指向内存地址,最后在初始化对象,
   那么就有可能造成实例不同的情况

 volatile关键字是防止JVM优化被禁止重排序,解决了重新排序的问题

¿Qué son las operaciones atómicas en Java?

"La operación atómica no necesita estar sincronizada". La llamada operación atómica se refiere a una operación que no será interrumpida por el mecanismo de programación de subprocesos; una vez que esta operación comience, se ejecutará hasta el final sin ningún contexto intermedio. Cambiar ; de la red

¿Por qué utilizar el bloqueo de doble verificación?

当同步的情况下线程A和线程B同时到3时,因为是同步所以线程A先进去创建对象,
 
线程B在外面等待,当线程A结束后,线程B在进入,当线程B走到4时判断instance == null

此时实例已经创建,则不在重新创建实例
 
当再有新的线程创建的时候,此时实例已经创建完成,

走到2时,则直接返回当前实例,不进行线程同步

Verifique el código de referencia de bloqueo:


    public static InstanceMode06 getInstance() {
    
    /*1*/
        if (instance == null) {
    
      /*2*/
            synchronized (InstanceMode06.class) {
    
    /*3*/
                if (instance == null) {
    
    /*4*/
                    instance = new InstanceMode06(); /*5*/
                }
            }
        }
        return instance;
    }

Comparar HashCode ():

效果图(1.6):

Recomendado para usar ☺

Clase interna estática

public class InstanceMode07 {
    
    
    /**
     * 私有化构造器 外部不能  new
     */
    private InstanceMode07(){
    
    }

    private static InstanceMode07 instance;

    //提供一个共有方法 使用到时才去创建intance
    public static synchronized InstanceMode07 getInstance(){
    
    
        return  SingletonInstance.INSTANCE;
    }

    //静态内部类
    public  static class SingletonInstance{
    
    
        private final static InstanceMode07 INSTANCE = new InstanceMode07();
    }
}

ventaja:

  • No se creará directamente cuando se cargue la clase, lo que garantiza que las
    propiedades estáticas de la clase con carga diferida solo se inicializarán cuando se cargue la clase.
    Debido a que se usa la clase interna, el hilo no puede ingresar cuando se inicializa la clase, que garantiza la seguridad del hilo (mecanismo JVM)

Comparar HashCode:

效果图(1.7):

Uso recomendado

enumerar

public  enum  InstanceMode08 {
    
    
     INSTANCE;
     public int showEnum(){
    
    
         return 10;
     }
}

Con el modo singleton implementado por jdk1.5, no solo se pueden evitar los problemas de subprocesos múltiples, sino que también se pueden evitar problemas como la deserialización y la recreación de objetos

Comparar HashCode:

效果图(1.8):


<< Effective java >> autor josh Bloch aboga por el camino

Nota:
Estoy usando el proyecto de Android. Aquellos que nunca hayan aprendido Android pueden descargar el código directamente. Puedes descargar el proyecto y estás ejecutando ~

Código completo

Artículos Recientes:

El patrón de fábrica del patrón de diseño de Java (2)

Patrón de prototipo del patrón de diseño de Java (3)

El patrón de constructor del patrón de diseño de Java (4)

Patrón de adaptador del patrón de diseño de Java (5)

Ir a la página del catálogo de patrones de diseño / principios de diseño

Es 2021/6. En un futuro cercano, haré todo lo posible para actualizar los 23 patrones de diseño, así que estad atentos ~

La originalidad no es fácil, tus gustos son tu mayor apoyo para mí, por favor, me gustaría apoyarme ~

Supongo que te gusta

Origin blog.csdn.net/weixin_44819566/article/details/112280415
Recomendado
Clasificación