Thread safe Singleton en Java

Singleton UML class diagram

El patrón de diseño Singleton nos permite asegurar que únicamente va a existir una instancia de una clase durante la ejecución de una aplicación, pero ¿cómo podemos asegurar que sólo se crea una instancia en un entorno multithreading?

La respuesta más sencilla es sincronizar el método getInstance(), pero haciendo esto obligaremos a que cualquier hilo que intente invocar este método deba esperar si otro hilo ya lo ha invocado, y además la sincronización es una práctica costosa y con un alto consumo de recursos. Teniendo en cuenta que sólo nos haría falta sincronizar el método al crear la instancia y que ya no sería necesario en las siguientes invocaciones, esta sería una solución poco optimizada.

La primera posible solución que podríamos aplicar es inicializar la instancia en la declaración static, de forma que en cuanto la clase fuera cargada por la JVM el objeto sería instanciado, y a partir de ahí cualquier llamada al método getInstance() devolvería esa misma instancia.

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

Doble comprobación de bloqueo (double checked locking) usando la palabra clave volatile

La solución anterior sería una opción, pero sería mucho más correcto realizar una doble comprobación de bloqueo. Con esto primero comprobaríamos si el objeto está sin instanciar, si es así realizaríamos la sincronización, y después volveríamos a comprobar si está sin inicializar.

public class SingletonClass {
    private static SingletonClass instance = null;
    private SingletonClass() {}

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

La doble comprobación la realizamos porque dos hilos podrían llamar a la vez al método. Ambos dos comprobarían que la instancia es null, pero uno de ellos esperará a que el otro termine de ejecutar el bloque synchronized. Cuando este termine el segundo entrará en el bloque y volverá a comprobar si es null. Como ya no lo es porque el anterior hilo ya ha instanciado el objeto, saldrá del bloque y devolverá la misma instancia.

También se podría realizar la comprobación dentro del bloque synchonized únicamente y el efecto sería el mismo, pero comprobando la condición antes, nos evitamos sincronizaciones innecesarias que supondrían un coste extra.

2 Comments

  1. Para escribir correctamente el método, se debe escribir la firma con el tipo de dato devuelto,
    public static SingletonClass getInstance() { .

    }

  2. Hola!

    Pero “double checked locking” es un Anti patrón. De hecho el código anterior donde se declara e instancia “inline” es la mejor opción como dices. El implementarlo como double checked locking se considera mala práctica, no?

Anímate a compartir con nosotros tus inquietudes y experiencias.

A %d blogueros les gusta esto: