Pregunta

Estoy mirando algún código en nuestra aplicación que creo que puede estar encontrando un caso "Bloqueo de dos verificaciones". He escrito algún código de muestra que sea similar a lo que hacemos.

¿Alguien puede ver cómo esto puede estar experimentando un bloqueo a doble verificación? ¿O es esto seguro?

class Foo {
    private Helper helper = null;
    public Helper getHelper() {
        Helper result;
        synchronized(this) {
            result = helper;
        }

        if (helper == null) {
            synchronized(this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
        return helper;
    }
}

Código base prestado del wiki.

¿Fue útil?

Solución

El punto completo de bloqueo a doble verificación es que la ruta rápida (cuando no necesita instanciar el objeto) no está sincronizado. Entonces, lo que tienes no es un bloqueo de doble verificación.

Debe deshacerse del primer bloque sincronizado para obtener un bloqueo de doble verificación roto. Entonces necesitas hacer helper volátil para arreglarlo.

Otros consejos

Es innecesariamente complejo, la forma más simple 'segura' de hacer DCL es así:

class Foo {
  private volatile Helper helper = null;
  private final Object mutex = new Object(); 
  public Helper getHelper() {
    if (helper == null) {
        synchronized(mutex) {
            if (helper == null) {
                helper = new Helper();
            }
        }
    }
    return helper;
  }
}

Los puntos clave aquí son:

  • En el caso 'feliz', esperamos que Helper ya sea asignado, por lo que si es así, podemos devolverlo sin tener que ingresar un bloque sincronizado.
  • Helper está marcado como volátil para hacerle saber al compilador que Helper puede ser leído / escrito por cualquier hilo en cualquier momento y es importante que las lecturas / escrituras no se reordenen.
  • El bloque sincronizado utiliza una variable final privada para sincronizar para evitar un posible golpe de rendimiento en el caso de otra área de código sincronización en el this instancia.

Esta parte es un bloqueo estándar de doble verificación:

if (helper == null) {
    synchronized(this) {
        if (helper == null) {
            helper = new Helper();
        }
    }
}

La primera parte es una tarea inútil que no hace nada a la parte de bloqueo de doble verificación: si Helper es nulo, entonces se ejecuta de todos modos, si no es así, no se ejecutaría de todos modos. Es completamente ineficaz.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top