Todos están familiarizados con el modelo Singleton, y todos saben por qué es perezoso, hambriento, etc. ¿Pero tienes una comprensión profunda del patrón de singleton? Hoy te llevaré a ver los singletons en mis ojos, que pueden ser diferentes de tu comprensión.
Aquí hay un pequeño ejemplo simple:
// clase pública simple y perezosa Singleton {// Singleton Instance Variable private static singleton instancia = null; // Método de construcción privada para garantizar que las clases externas no se puedan instanciar a través del constructor privado singleton () {} // Obtener instancia singleton public static singleton getInstance () {if (instance == null) {instancia = nueva singleton (); } System.out.println ("¡Soy un simple singleton perezoso!"); instancia de retorno; }}Es fácil ver que el código anterior no es seguro en el caso de múltiples subprocesos. Cuando dos hilos ingresan if (instancia == nulo), ambos hilos juzgan que la instancia está vacía, y luego se obtendrán dos instancias. Este no es el singleton que queremos.
A continuación, utilizamos el bloqueo para lograr la exclusión mutua para garantizar la implementación de singletons.
// Método sincrónico Lazy Public Class Singleton {// Singleton Instance Variable private static singleton instancia = null; // Método de construcción privada para garantizar que las clases externas no se puedan instanciar a través de constructor privado singleton () {} // obtener instancia singleton public static sincronizado sincronizado sincronizado getInstance () {if (instance == null) {instancia = nueva singleton (); } System.out.println ("Soy un método sincrónico Singleton!"); instancia de retorno; }}Agregar sincronizado garantiza la seguridad de los subprocesos, pero ¿es esta la mejor manera? Obviamente no lo es, porque de esta manera, cada vez que llamamos método getInstance (), estaremos bloqueados y solo necesitamos bloquearlo cuando llamamos a getInstance () la primera vez. Obviamente, esto afecta el rendimiento de nuestro programa. Continuamos encontrando mejores maneras.
Después del análisis, se descubrió que solo asegurando que Instance = new Singleton () sea exclusión mutua de subprocesos, se puede garantizar la seguridad del hilo, por lo que la siguiente versión está disponible:
// Bloqueo doble de clase pública Lazy Singleton {// Singleton Instance Variable private static singleton instancia = null; // Método de construcción privada para garantizar que las clases externas no se puedan instanciar a través de Constructor Private Singleton () {} // Get Singleton Instance public static singleton getInstance () {if (instance == null) {sincronizado (singleton.class) {if (instancia == null) {instancia = new singeton (); }}} System.out.println ("¡Soy un singleton perezoso de doble bloqueo!"); instancia de retorno; }} Esta vez parece que no solo resuelve el problema de seguridad del hilo, sino que no hace que se agregue el bloqueo cada vez que llame a getInstance (), lo que resulta en la degradación del rendimiento. Parece una solución perfecta, ¿es realmente así?
Desafortunadamente, el hecho no es tan perfecto como pensábamos. Hay un mecanismo llamado "escrituras fuera de orden" en el modelo de memoria de la plataforma Java. Es este mecanismo el que causa la falla del método de bloqueo de doble verificación. La clave de este problema se encuentra en la línea 5 en el código anterior: instancia = new Singleton (); Esta línea en realidad hace dos cosas: 1. Llame al constructor y cree una instancia. 2. Asigne esta instancia a la instancia de la variable de instancia. Pero el problema es que estos dos pasos de JVM no garantizan el pedido. Es decir. Puede ser que la instancia se haya establecido en no vacío antes de llamar al constructor. Analicémoslo juntos:
Supongamos que hay dos hilos A y B
1. El hilo A ingresa al método getInstance ().
2. Debido a que la instancia está vacía en este momento, el hilo A ingresa al bloque sincronizado.
3. Thread a ejecuta instancia = new Singleton (); Establece la instancia de la variable de instancia en no temply. (Tenga en cuenta que es antes de llamar al constructor).
4. El hilo A sale y el hilo B entra.
5. El hilo B verifica si la instancia está vacía y no está vacía en este momento (en el tercer paso, está configurado como no vacío por el hilo A). El hilo B devuelve una referencia a la instancia. (El problema surge. En este momento, la referencia a la instancia no es una instancia de singleton porque el constructor no se llama).
6. El hilo B sale y el hilo A entra.
7. El hilo A continúa llamando al método del constructor, completa la inicialización de la instancia y devuelve.
¿No hay una buena manera? Debe haber una buena manera, ¡sigamos explorando!
// Resuelve el problema de la escritura no ordenada de la clase pública Lazy Singleton {// Singleton Instance Variable private static singleton instancia = null; // Método de construcción privada para garantizar que las clases externas no se puedan instanciar a través de Constructor Private Singleton () {} // Obtener instancia singleton public static singleton getInstance () {if (instance == null) {sincronizado (singleton.class) {// 1 Temp = instancia; // 2 if (temp == null) {sincronizado (singleton.class) {// 3 temp = new Singleton (); // 4} instancia = temp; // 5}}} system.out.println ("¡Estoy resolviendo la escritura desordenada de singletons perezosos!"); instancia de retorno; }} 1. El hilo A ingresa al método getInstance ().
2. Debido a que la instancia está vacía, el hilo A entra en el primer bloque sincronizado en la posición // 1.
3. Enhebre A ejecuta el código en la posición // 2 y asigna instancia a la temperatura variable local. La instancia está vacía, por lo que la temperatura también está vacía.
4. Debido a que la temperatura está vacía, el hilo A entra en el segundo bloque sincronizado en la posición // 3. (Pensé que esta cerradura era un poco redundante)
5. Enhebre A ejecuta el código en la posición // 4, estableciendo temperatura a no vacía, ¡pero el constructor aún no se ha llamado! (Problema de "escritura de desordenamiento")
6. Si el hilo A bloquea, el hilo B ingresa al método getInstance ().
7. Debido a que la instancia está vacía, el hilo B intenta ingresar el primer bloque sincronizado. Pero porque el hilo A ya está adentro. Entonces es imposible entrar. Bloques B de hilo.
8. El hilo A se activa y continúe ejecutando el código en la posición // 4. Llame al constructor. Generar una instancia.
9. Asigne la referencia de instancia de TEMP a instancia. Salir dos bloques sincronizados. Devuelve la instancia.
10. El hilo B se activa y entra en el primer bloque sincronizado.
11. El hilo B ejecuta el código en la posición // 2 y asigna la instancia de instancia a la variable Local TEMP.
12. El hilo B determina que la temperatura variable local no está vacía, así que omita el bloque if. Devuelve la instancia de instancia.
Hasta ahora, hemos resuelto el problema anterior, pero de repente descubrimos que para resolver el problema de seguridad del hilo, parece que hay mucho hilo envuelto en el cuerpo ... es desordenado, por lo que necesitamos racionalizarlo:
// Hungry Public Class Singleton {// Singleton Variable, estática, se inicializa una vez cuando la clase se carga para garantizar la seguridad de la seguridad de subprocesos Singleton Instance = new Singleton (); // Método de construcción privada para garantizar que las clases externas no se puedan instanciar a través del constructor. Private Singleton () {} // Obtener la instancia de objeto Singleton public static singleton getInstance () {System.out.println ("¡Soy un singleton hambriento!"); instancia de retorno; }}Cuando vi el código anterior, instantáneamente sentí que el mundo estaba en silencio. Sin embargo, este método adopta el método de estilo hombre hambriento, que es para decidir objetos singleton. Una desventaja de esto es: si el singleton de la construcción es grande y no se usa después de completar la construcción, conducirá a un desperdicio de recursos.
¿Hay una manera perfecta? Continuar mirando:
// La clase interna implementa perezosa clase pública Singleton {private static class singletonholder {// singleton variable private static singleton instancia = new Singleton (); } // Método de construcción privada para garantizar que las clases externas no se puedan instanciar a través del constructor. private singleton () {} // Object OBJET SINGLETON Public static singleton getInstance () {System.out.println ("¡Soy un singleton de clase interna!"); devolución singletonholder.Instance; }}Perezoso (evite los desechos de recursos anteriores), el código seguro y el código simple. Debido a que el mecanismo Java estipula que el Singleton Holder de clase interna solo se cargará cuando el método getInstance () se solicite por primera vez (implementando perezoso), y su proceso de carga es seguro de hilo (implementando hilo-aficionado). La instancia se instancia cuando se carga la clase interna.
Hablemos brevemente sobre la escritura desordenada mencionada anteriormente. Esta es la característica de JVM. Por ejemplo, declarando dos variables, cadena A; Cadena B; JVM puede cargar un primero o b. Del mismo modo, instancia = new Singleton (); puede establecer la instancia en no vacío antes de llamar al constructor de Singleton. Esta es una pregunta para muchas personas, diciendo que un objeto de singleton no ha sido instanciado, entonces, ¿cómo se volvió la instancia no vacía? ¿Cuál es su valor ahora? Si desea comprender este problema, debe comprender cómo la oración instancia = new Singleton (); se ejecuta. Aquí hay un pseudocódigo para explicarte:
mem = asignA (); // Asignar memoria para objetos Singleton. instancia = mem; // Tenga en cuenta que la instancia no está vacía ahora, pero aún no se ha inicializado. ctorsingleton (instancia); // Llame al constructor de Singleton y pase la instancia.
Se puede ver que cuando un hilo ejecuta instancia = mem;, instancia no está vacía. Si otro hilo ingresa al programa y a la instancia de jueces como no vacío, saltará para devolver la instancia; Y en este momento, el constructor de Singleton no ha llamado instancia, y el valor actual es el objeto de memoria devuelto por asign (); Entonces, el segundo hilo no tiene un objeto Singleton, sino un objeto de memoria.
Lo anterior son mis pequeños pensamientos y comprensión del modelo Singleton. Doy la bienvenida a todos los grandes dioses para que vengan y guíen y critiquen.