2008-10-01 12 views
11

estaba leyendo this article de "bloqueo doble comprobación" y fuera del tema principal del artículo Me preguntaba por qué en algún momento del artículo, el autor utiliza la siguiente Idioma:doble comprobación de bloqueo artículo

listado 7. el 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 algo de código con la misma cerradura? ¿Tiene esto algún propósito?

Muchas gracias de antemano.

+0

Hay una pregunta relacionada "[Mejor Implementación de Singleton en Java] (http://stackoverflow.com/questions/70689/best-singleton-implementation-in-java)" –

Respuesta

15

El punto de bloqueo dos veces fue intento para evitar escrituras fuera de orden. El modelo de memoria especifica dónde pueden ocurrir los cambios de orden, en parte en términos de bloqueos. El bloqueo garantiza que no aparezca ninguna escritura (incluida ninguna dentro del constructor singleton) después de "instancia = inst"; línea.

Sin embargo, para profundizar en el tema recomendaría Bill Pugh's article. Y luego nunca lo intentan :)

+0

Pero podría usar el Java 5 que realmente funciona variante al final del artículo. –

+1

@hstoerr: Yo personalmente no lo haría, a menos que descubriera que era el cuello de botella real en algún código. Es muy fácil equivocarse. Todo tiene su lugar, por supuesto, pero sería bastante raro para mí usarlo. –

0

bien, pero el artículo dice que

El código en el Listado 7 no funciona debido a la actual definición del modelo de memoria. La especificación de 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 los JVM hace la siguiente traducción de "pseudo-código" en la MAPE:

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 escribe después de la "instancia = inst "no se logra?

Voy a leer ahora el artículo, gracias por el enlace.

6

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

Ésta es insegura:

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 insegura:

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 hacer cualquiera de ellos, jamás.

En cambio, sincronice todo el método:

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

A menos que estés recuperar este objeto un trillón de veces por segundo el impacto en el rendimiento, en términos reales, es despreciable.

13

El artículo hace referencia al modelo de memoria Java anterior a la 5.0 (JMM).Según ese modelo, al salir de un bloque sincronizado, se escribe escritura forzada en la memoria principal. Por lo tanto, parece ser un intento de asegurarse de que el objeto Singleton se expulsa antes de la referencia a él. Sin embargo, no funciona porque la escritura a instancia se puede mover al bloque: el motel Roach.

Sin embargo, el modelo anterior a 5.0 nunca se implementó correctamente. 1.4 debe seguir el modelo 5.0. Las clases se inicializan con pereza, por lo que puede ser que también acaba de escribir

public static final Singleton instance = new Singleton(); 

O mejor, no utilizan únicos porque ellos son malos.

+0

+1 para inicializadores estáticos –

0

En cuanto a este idioma no es un artículo muy recomendable y aclarar:

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

Por otro lado, creo que lo dhighwayman.myopenid medios es por eso que el escritor ha puesto un bloque sincronizado en referencia a la misma clase (sincronizada (Singleton.class)) dentro de otro bloque sincronizado que hace referencia a la misma clase. Puede suceder cuando se crea una nueva instancia (Singleton inst = instance;) dentro de ese bloque y para garantizar que sea seguro para subprocesos, es necesario escribir otro sincronizado.

De lo contrario, no veo ningún sentido.

1

Tras la Recomendación John Skeet:

Sin embargo, para profundizar en el tema te recomiendo el artículo de Bill Pugh. Y a continuación, No se debe tratar it :)

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

Este código pone construcción del objeto ayudante dentro de un bloque sincronizado interior. La idea intuitiva aquí es que debe haber una barrera de memoria en el punto donde se libera sincronización, y que debe evitar que el reordenamiento del inicialización del objeto ayudante y la asignación al campo ayudante.

Así que, básicamente, con el bloque de sincronización interna, que están tratando de "engañar" al JMM crear la instancia dentro del bloque de sincronización, para obligar a la JMM para ejecutar esa asignación antes de que el bloque de sincronización terminado. Pero el problema aquí es que el JMM nos dirige y está moviendo la asignación que está antes del bloque de sincronización dentro del bloque de sincronización, trasladando nuestro problema al principio.

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

0

Ver el Google Tech Talk en el Java Memory Model para una muy buena introducción a los puntos más finos de la JMM. Dado que falta aquí, también me gustaría señalar el blog de Jeremy Mansons 'Java Concurrency' esp. la publicación en Double Checked locking (cualquiera que esté en el mundo de Java parece tener un artículo sobre esto :).

0

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 Double-Checked Locking Declaration:

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 volátil en la declaración de variables - de lo contrario no funciona y no funciona en Java 1.4 o menos, de todos modos.

+0

sí, verifique la respuesta que he seleccionado como correcta. –

Cuestiones relacionadas