El propósito del patrón Singleton es garantizar que una clase tenga solo una instancia y también proporcione un punto de acceso global para ello. Para evitar que otros trabajadores instancionen nuestra clase,
Puede crear un constructor único para esta clase y establecer la visible del constructor en privado. Vale la pena señalar que si creamos otros constructores no privados, o no hacemos ninguna mención de la clase en absoluto
Para los constructores, entonces otras personas aún pueden instanciar nuestra clase. Si no queremos crear un objeto singleton de antemano, podemos esperar hasta la primera vez que usamos el objeto Singleton, es decir,
Inicialización de retraso. Hay dos razones para retrasar la inicialización de los objetos singleton:
1. Tal vez en el tiempo de inicialización estática, no tiene suficiente información sobre cómo inicializar un objeto Singleton.
2. El propósito de seleccionar un singleton de inicialización de retraso puede ser esperar recursos como conexiones de bases de datos, especialmente en aplicaciones donde no se requiere este singleton en ciertas sesiones específicas.
Si un singleton se inicializa en un entorno multiproceso, debemos tener cuidado de evitar que múltiples hilos inicialicen al mismo tiempo.
Por lo general, el patrón singleton está construido en el idioma Java:
Way Lazy: se refiere a la instancia global de Singleton que se está construyendo cuando se usa por primera vez. Retrasar la inicialización.
Método Hungry Man: se refiere a la instancia única global que se está construyendo durante la carga de clase. Inicialización urgente.
1. Singleton chino hambriento
public class Singleton1 {private Singleton1 () {} // Defina su propia instancia internamente. // Tenga en cuenta que esto es privado. Instancia privada estática Singleton1 = new Singleton1 (); /** * // ** * Aquí hay un método estático para el acceso externo a esta clase, a la que se puede acceder directamente a * @return */public static singleton1 getInstance () {instancia de retorno; }}2. Clase de singleton perezoso
public class Singleton2 {private static singleton2 instance = null; /*** // *** Este método se mejora en comparación con lo anterior. No requiere generar objetos cada vez, pero la primera vez * genera instancias cuando se usa, ¡lo que mejora la eficiencia! * @return */ public static singleton2 getInstance () {if (instance == null) instancia = new Singleton2 (); instancia de retorno; }}Los siguientes son los principales problemas de subprocesos múltiples. En los singletons perezosos, no hay problema con el enhebrado único, pero cuando el subproceso múltiple, puede haber dos o más instancias de Singletion2.
Por ejemplo: cuando Thread 1 juzga esa instancia == NULL es verdadera, al escanear la nueva operación, antes de realizar la nueva operación y después de realizar la nueva operación, Thread 2 simplemente realiza la operación del juicio, y la instancia aún es nula. Por lo tanto, el hilo 2 también realizará la nueva operación. Y así sucesivamente, bajo alta concurrencia, puede haber dos o más instancias de Singletion2. Obviamente, esto es incorrecto.
Por lo tanto, cambie el código de la siguiente manera:
clase pública singleton3 {private static singleton3 instancia = nulo; /*** // *** Este método se mejora en comparación con lo anterior. No requiere que el objeto se genere cada vez, pero la primera vez * genera instancias cuando se usa, lo que mejora la eficiencia. * Para evitar errores en múltiples subprocesos, se agregó el indicador de sincronización * @return */ public static sincronizado sincronizado 3 getInstance () {if (instance == null) instancia = nueva singleton3 (); instancia de retorno; }}Pero esto crea otro problema. Los métodos se sincronizan cada vez que se recupera la instancia. Obviamente, el rendimiento está muy afectado, así que continúe cambiando el código de la siguiente manera:
volátil, reemplace la sincronización con un costo más bajo
¿Por qué es volátil más barato que la sincronización?
El costo de la sincronización está determinado principalmente por su rango de cobertura. Si se puede reducir el rango de cobertura de sincronización, el rendimiento del programa puede mejorarse enormemente.
La cobertura de Volátil está solo en el nivel variable. Por lo tanto, su costo de sincronización es muy bajo.
¿Cuál es el principio de volátil?
La semántica de Volátil es en realidad decirle al procesador que no me ponga en la memoria de trabajo, por favor opere directamente en la memoria principal. (Consulte el modelo de memoria Java para la memoria de trabajo para más detalles)
Por lo tanto, cuando el múltiple o múltiples subconjuntos accede a la variable, operarán directamente la memoria principal, lo que esencialmente logra el intercambio de variables.
¿Cuáles son las ventajas de volátiles?
1. Rendimiento del programa más grande
2. Menos código para implementar múltiples subprocesos
3. El programa tiene una mejor escalabilidad
4. Es más fácil de entender, y no hay necesidad de costos de aprendizaje demasiado altos.
¿Cuáles son las desventajas de Volátil?
1. Propenso a problemas
2. Es difícil de diseñar
Volatile usa JDK requiere la versión 1.5 y superior.
El código mejorado es el siguiente (también llamado doble bloqueo):
Class Public Singleton4 {instancia de soltero volátil estático privado4; /*** // *** Bloqueo doble para lograr la optimización de aplicaciones y rendimiento de múltiples subconjuntos* @return*/public static singleton4 getInstance () {if (instancia == null) {sincronizado (singleton4.class) {// 1 if (instancia == null) // 2 instance = new Singleton4 (); // 3}} instancia de retorno; }}