Pregunta

Estaba leyendo este artículo sobre " Doble -Comprobado el bloqueo " y fuera del tema principal del artículo, me preguntaba por qué, en algún momento del artículo, el autor utiliza el siguiente Idioma:

  

Listado 7. Intento de resolver el problema de escritura fuera de orden

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

Y mi pregunta es: ¿Hay alguna razón para sincronizar dos veces un código con el mismo bloqueo? ¿Tiene esto algún propósito?

Muchas gracias de antemano.

¿Fue útil?

Solución

El punto de bloqueo dos veces era intentar para evitar escrituras fuera de orden. El modelo de memoria especifica dónde pueden ocurrir reordenamientos, en parte en términos de bloqueos. El bloqueo garantiza que no se produzcan escrituras (incluidas las que están dentro del constructor singleton) después de que " instance = inst; " línea.

Sin embargo, para profundizar en el tema, recomiendo Artículo de Bill Pugh . Y luego nunca lo intentes :)

Otros consejos

El artículo hace referencia al modelo de memoria Java (JMM) anterior a 5.0. Bajo ese modelo, dejando un bloque sincronizado forzado se escribe en la memoria principal. Por lo que parece ser un intento de asegurarse de que el objeto Singleton se saque antes de la referencia a él. Sin embargo, no funciona del todo porque la escritura en la instancia se puede mover al bloque: el motel de cucarachas.

Sin embargo, el modelo pre-5.0 nunca se implementó correctamente. 1.4 debe seguir el modelo 5.0. Las clases se inician de forma perezosa, por lo que también puedes escribir

public static final Singleton instance = new Singleton();

O mejor, no uses singletons porque son malos.

Jon Skeet tiene razón: lea Bill Pugh's artículo. El idioma que utiliza Hans es la forma precisa en que no funcionará , y no debe utilizarse.

Esto no es seguro:

private static Singleton instance;

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

Esto también es inseguro:

public static Singleton getInstance()  
{
    if (instance == null)
    {
        synchronized(Singleton.class) {      //1
            Singleton inst = instance;         //2
            if (inst == null)
            {
                synchronized(Singleton.class) {  //3
                    inst = new Singleton();        //4
                }
                instance = inst;                 //5
            }
        }
    }
    return instance;
}

No hagas ninguno de ellos, nunca.

En su lugar, sincronice todo el método:

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

A menos que esté recuperando este objeto un millón de veces por segundo, el impacto en el rendimiento, en términos reales, es insignificante.

Siguiendo la John Skeet Recomendación:

  

Sin embargo, para profundizar en el tema   Recomendaría el artículo de Bill Pugh. Y   entonces nunca lo intentes :)

Y aquí está la clave para el segundo bloque de sincronización:

  

Este código pone construcción de la   Objeto ayudante dentro de un interior.   Bloque sincronizado. La idea intuitiva   Aquí es que debería haber un recuerdo.   barrera en el punto donde   se libera la sincronización, y que   debe evitar el reordenamiento de la   Inicialización del objeto ayudante.   y la asignación al campo.   ayudante.

Básicamente, con el bloque de sincronización interna, estamos tratando de " cheat " el JMM que crea la instancia dentro del bloque de sincronización, para forzar al JMM a ejecutar esa asignación antes de que finalice el bloque de sincronización. Pero el problema aquí es que el JMM nos está dirigiendo y está moviendo la tarea que está antes del bloque de sincronización dentro del bloque de sincronización, moviendo nuestro problema de nuevo al principio.

Esto es lo que entendí de esos artículos, realmente interesante y una vez más, gracias por las respuestas.

Está bien, pero el artículo dice que

  

El código en el Listado 7 no funciona debido a la definición actual del modelo de memoria. La especificación del lenguaje Java (JLS) exige que el código dentro de un bloque sincronizado no se mueva fuera de un bloque sincronizado. Sin embargo, no dice que el código que no está en un bloque sincronizado no se puede mover a un bloque sincronizado.

Y también parece que la JVM hace la próxima traducción a " pseudo-código " en ASM:

public static Singleton getInstance()
{
  if (instance == null)
  {
    synchronized(Singleton.class) {      //1
      Singleton inst = instance;         //2
      if (inst == null)
      {
        synchronized(Singleton.class) {  //3
          //inst = new Singleton();      //4
          instance = new Singleton();               
        }
        //instance = inst;               //5
      }
    }
  }
  return instance;
}

Hasta ahora, el punto de no escritura después de la " instance = inst " no se cumple?

Leeré ahora el artículo, gracias por el enlace.

Desde Java 5, puede hacer que el bloqueo funcione con doble verificación declarando que el campo es volátil.

Consulte http://www.cs.umd.edu /~pugh/java/memoryModel/DoubleCheckedLocking.html para una explicación completa.

Respecto a este idioma, hay un artículo muy recomendable y clarificador:

http: // www .javaworld.com / javaworld / jw-02-2001 / jw-0209-double.html? page = 1

Por otra parte, creo que dhighwayman.myopenid significa que el escritor ha puesto un bloque sincronizado que se refiere a la misma clase (sincronizado (Singleton.class)) dentro de otro bloque sincronizado que se refiere a la misma clase. Puede ocurrir cuando se crea una nueva instancia (Singleton inst = instance;) dentro de ese bloque y para garantizar que sea segura para subprocesos, es necesario escribir otra sincronización.

De lo contrario, no puedo ver ningún sentido.

Consulte Google Tech Talk en Java Memory Model para una introducción realmente agradable. a los puntos más finos de la JMM. Como falta aquí, también me gustaría señalar el blog de Jeremy Mansons 'Java Concurrency' esp. la publicación en Bloqueo de doble comprobación (cualquiera que tenga algo en El mundo de Java parece tener un artículo sobre esto :).

Para Java 5 y mejor, en realidad hay una variante de doble comprobación que puede ser mejor que sincronizar todo el descriptor de acceso. Esto también se menciona en el Declaración de bloqueo de doble comprobación :

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

La diferencia clave aquí es el uso de volatile en la declaración de la variable. De lo contrario, no funciona y, de todos modos, no funciona en Java 1.4 o menos.

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