ProgramaciónDesarrollador backend de Java

¿Cómo implementar el patrón Singleton en Java teniendo en cuenta la multihilo y la serialización? ¿Qué matices existen en la implementación?

Supere entrevistas con el asistente de IA Hintsage

Respuesta

El Singleton clásico garantiza la creación de solo una instancia de un objeto. En Java hay varias formas de implementación, pero teniendo en cuenta la multihilo y la serialización, se deben considerar algunos matices:

  1. Implementación Thread-safe (Double-checked Locking): Se utiliza a menudo para la inicialización perezosa.
public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
  1. Enum Singleton: La mejor manera en términos de protección contra la serialización, reflexión y multihilo.
enum EnumSingleton { INSTANCE; // métodos }
  1. Problemas con la serialización: Un Singleton normal puede perder su unicidad después de la serialización/deserialización. Para un funcionamiento correcto, es necesario agregar el método readResolve():
private Object readResolve() { return getInstance(); }

Pregunta trampa

¿Es suficiente utilizar el método synchronized getInstance() para un Singleton thread-safe?

Respuesta: Sí, pero este enfoque conduce a una disminución del rendimiento, porque synchronized se llama en cada acceso a getInstance. Es más eficaz usar Double-checked Locking + volatile para instance, o usar Enum para implementar Singleton.

Ejemplo de código ineficiente:

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

Historia

En un sistema financiero, al implementar el singleton se olvidó añadir volatile a la referencia instance en el double-checked locking. Como resultado, bajo alta carga, ocurrieron casos aleatorios de creación de dos instancias de clase, lo que llevó a la inconsistencia en los informes.


Historia

En una biblioteca de registro, implementaron Singleton a través de un objeto estático privado, sin embargo, durante la serialización y posterior deserialización (por ejemplo, en un entorno en clúster) surgieron varias instancias. El problema se resolvió añadiendo readResolve().


Historia

En un sistema de análisis, un desarrollador implementó un Singleton thread-safe a través del método synchronized getInstance(). En un sistema de alta carga, en el momento pico, hubo una caída brusca del rendimiento; se descubrió que las llamadas a getInstance() (miles de veces por segundo) se bloqueaban entre sí debido a una sincronización innecesaria, aunque la inicialización solo necesita hacerse una vez.