Modelo Singleton: requiere solo una instancia de una clase y proporciona métodos de acceso global.
1. Modelos singleton perezosos y hambrientos
1.1 Slacker
El patrón de singleton diferido se instancia mediante inicialización estática cuando se carga la clase.
Layzerbones de clase pública {Layzerbones
estáticos privados layzer = new Layzerbones ();
Layzerbones privados () {}
Layzerbones estáticos públicos getInstance () {
return layzer;
}
}
1.2 Hambriento
El modo singleton hambriento se instancia cuando se llama por primera vez;
Badmash de clase pública {
Badmash estático privado badmash;
Badmash privado () {}
Badmash público estático sincronizado getInstance () {// 方法 一
if (badmash == null) {
badmash = new Badmash ();
}
return badmash;
}
public static Badmash getInstance2 () {// 方法 二
if (badmash == null) {
sincronizado (Badmash.class) {
if (badmash == null) {
badmash = new Badmash ();
}
}
}
return badmash;
}
}
Lo que los dos modelos singleton anteriores tienen en común :
1. La clase actual se usa como una variable miembro estática; 2. El constructor se privatiza; 3. Se proporciona un método de instanciación accesible y estático;
La diferencia es:
1. Estilo perezoso: seguridad del hilo;
2. El estilo Han hambriento, el hilo no es seguro, por lo que debemos aumentar el bloqueo;
3. Hay dos maneras de bloquear el estilo chino con hambre: una es agregar un bloqueo al método, la cerradura es un archivo de código de bytes de la clase, la otra es el bloque de código de sincronización y el bloqueo se puede personalizar
4. El propósito de la doble verificación de los bloques de código de sincronización es mejorar la eficiencia de la ejecución;
2. El problema y la optimización del bloqueo de doble verificación chino hambriento
El problema es que el compilador puede reordenar el código lógico existente durante la compilación;
Este es un dibujo en "JAVA Concurrent Programming Art", que ilustra claramente el problema, como se muestra a continuación;
El orden de inicialización de los objetos normales es: 1. Primero asigne espacio de memoria al objeto; 2. Inicialice el objeto; 3. Apunte la instancia inicializada a la dirección de memoria asignada.
Pero el problema es que debido a que la reordenación interna de JMM, la inicialización de objetos y los puntos de dirección de instancia a la dirección de memoria pueden reordenarse;
Luego habrá problemas: antes de que el objeto se haya inicializado, ya ha apuntado a la dirección de memoria, y el objeto es nulo; cuando otro hilo accede nuevamente, juzgará nuevamente, será nulo y reinicializará el objeto.
Por supuesto, para este tipo de problema, es mejor resolverlo. Agregue un modificador volátil al miembro estático . Este es un bloqueo liviano para hacer que la variable modificada sea atómica ;
Badmash estático volátil privado badmash;
Por supuesto, hay otras formas de evitar este problema.
3. Modelo de diseño de singleton interno estático
Para evitar el problema anterior de inicialización y reorganización de objetos, puede usar el principio de bloqueo de clase en el proceso de inicialización para implementar singletons a través de clases internas;
public class Singleton {
private Singleton () {
}
public static Singleton getInstance () {
return Inner.instance;
}
Private static class Inner {
private static final Singleton instancia = new Singleton ();
}
}
4. Enumerar el patrón singleton
1. Establezca la enumeración de instancias INSTINCT en la enumeración ;
2. Luego inicialice el objeto que necesita inicializarse en el constructor privado ;
3. Proporcione métodos externos no estáticos para obtener instancias
enumeración pública SingleEnum {INSTINCT
;
usuario usuario privado;
SingleEnum () {
usuario = nuevo Usuario ();
}
usuario público getInstance () {
usuario de retorno;
}
}
¡Lo anterior es el modelo singleton más común!
En este punto, está terminado!