thread safe pattern lazy examples best java multithreading synchronization singleton

safe - singleton synchronized java



Java Singleton y sincronización (8)

Por favor, aclare mis dudas con respecto a Singleton y Multithreading:

  • ¿Cuál es la mejor manera de implementar Singleton en Java, en un entorno multiproceso?
  • ¿Qué sucede cuando varios hilos intentan acceder al método getInstance() al mismo tiempo?
  • ¿Podemos synchronized getInstance() singleton?
  • ¿Es realmente necesaria la sincronización cuando se usan clases de Singleton?

¿Cuál es la mejor manera de implementar Singleton en Java, en un entorno multiproceso?

Consulte esta publicación para conocer la mejor forma de implementar Singleton.

¿Cuál es una forma eficiente de implementar un patrón singleton en Java?

¿Qué sucede cuando varios hilos intentan acceder al método getInstance () al mismo tiempo?

Depende de la forma en que haya implementado el método. Si usa el bloqueo doble sin variable volátil, puede obtener un objeto Singleton parcialmente construido.

Consulte esta pregunta para más detalles:

¿Por qué se usa volátil en este ejemplo de bloqueo doble verificado?

¿Podemos sincronizar getInstance () de singleton?

¿Es realmente necesaria la sincronización cuando se usan clases de Singleton?

No se requiere si implementa Singleton de las siguientes maneras

  1. entitalización estática
  2. enum
  3. LazyInitalaization con Initialization-on-demand_holder_idiom

Consulte esta pregunta para obtener más detalles

Patrón de diseño de Java Singleton: preguntas


Enum singleton

La forma más sencilla de implementar un Singleton que es seguro para subprocesos es utilizando un Enum

public enum SingletonEnum { INSTANCE; public void doSomething(){ System.out.println("This is a singleton"); } }

Este código funciona desde la introducción de Enum en Java 1.5

Doble bloqueo marcado

Si desea codificar un singleton "clásico" que funcione en un entorno multiproceso (a partir de Java 1.5), debe usar este.

public class Singleton { private static volatile Singleton instance = null; private Singleton() { } public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class){ if (instance == null) { instance = new Singleton(); } } } return instance ; } }

Esto no es seguro para subprocesos antes de 1.5 porque la implementación de la palabra clave volátil era diferente.

Carga temprana Singleton (funciona incluso antes de Java 1.5)

Esta implementación instancia el singleton cuando se carga la clase y proporciona seguridad de subprocesos.

public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } public void doSomething(){ System.out.println("This is a singleton"); } }


Este patrón realiza una inicialización lenta y segura de subprocesos de la instancia sin sincronización explícita.

public class MySingleton { private static class Loader { static final MySingleton INSTANCE = new MySingleton(); } private MySingleton () {} public static MySingleton getInstance() { return Loader.INSTANCE; } }

Funciona porque utiliza el cargador de clases para hacer toda la sincronización de forma gratuita: primero se accede a la clase MySingleton.Loader en el método getInstance() , por lo que la clase Loader carga cuando se llama a getInstance() por primera vez. Además, el cargador de clases garantiza que toda la inicialización estática se completa antes de que tengas acceso a la clase, eso es lo que te brinda seguridad en el hilo.

Es como magia.

En realidad, es muy similar al patrón enum de Jhurtado, pero creo que el patrón enum es un abuso del concepto enum (aunque funciona)


Sí, es necesario. Hay varios métodos que puede usar para lograr la seguridad del hilo con la inicialización lenta:

Sincronización draconiana:

private static YourObject instance; public static synchronized YourObject getInstance() { if (instance == null) { instance = new YourObject(); } return instance; }

Esta solución requiere que cada hilo esté sincronizado cuando en realidad solo los primeros necesitan ser sincronizados.

Verificación de doble sincronización :

private static final Object lock = new Object(); private static volatile YourObject instance; public static YourObject getInstance() { YourObject r = instance; if (r == null) { synchronized (lock) { // While we were waiting for the lock, another r = instance; // thread may have instantiated the object. if (r == null) { r = new YourObject(); instance = r; } } } return r; }

Esta solución garantiza que solo los primeros hilos que intenten adquirir su singleton deben pasar por el proceso de adquisición del bloqueo.

Inicialización a pedido :

private static class InstanceHolder { private static final YourObject instance = new YourObject(); } public static YourObject getInstance() { return InstanceHolder.instance; }

Esta solución aprovecha las garantías del modelo de memoria de Java sobre la inicialización de clase para garantizar la seguridad del hilo. Cada clase solo se puede cargar una vez, y solo se cargará cuando sea necesaria. Eso significa que se llama a la primera InstanceHolder getInstance , se cargará InstanceHolder y se creará la instance , y dado que esto es controlado por ClassLoader s, no es necesaria ninguna sincronización adicional.


Sí, necesita sincronizar getInstance() . Si no es así, podría surgir una situación en la que se puedan realizar varias instancias de la clase.

Considere el caso donde tiene dos hilos que llaman getInstance() al mismo tiempo. Ahora imagine que T1 se ejecuta justo después de la instance == null verificación instance == null , y luego se ejecuta T2. En este momento, la instancia no se crea o establece, por lo que T2 pasará la verificación y creará la instancia. Ahora imagine que la ejecución vuelve a T1. Ahora se crea el singleton, pero T1 ya ha realizado el control. ¡Procederá a hacer el objeto otra vez! Hacer que getInstance() sincronizado evita este problema.

Hay algunas maneras de hacer singletons thread-safe, pero sincronizar getInstance() es probablemente el más simple.


Si está trabajando en un entorno multiproceso en Java y necesita garantizar que todos los hilos estén accediendo a una sola instancia de una clase, puede usar un Enum. Esto tendrá la ventaja adicional de ayudarlo a manejar la serialización.

public enum Singleton { SINGLE; public void myMethod(){ } }

y luego simplemente haz que tus hilos usen tu instancia como:

Singleton.SINGLE.myMethod();


También puede usar el bloque de código estático para instanciar la instancia en la carga de la clase y evitar los problemas de sincronización del hilo.

public class MySingleton { private static final MySingleton instance; static { instance = new MySingleton(); } private MySingleton() { } public static MySingleton getInstance() { return instance; } }


public class Elvis { public static final Elvis INSTANCE = new Elvis(); private Elvis () {...} }

Fuente: Java efectivo -> Artículo 2

Sugiere usarlo, si está seguro de que la clase siempre será singleton.