22
private volatile static Singleton uniqueInstance 

En un singleton cuando se utiliza el método de bloqueo doble para la sincronización, ¿por qué la instancia única se declara como volátil? ¿Puedo lograr la misma funcionalidad sin declararla como volátil?¿Cuál es el punto de hacer que la instancia de singleton sea volátil mientras se usa el bloqueo doble?

+0

Puestos relacionados con [aquí] (https : //stackoverflow.com/q/18093735/465053) y [aquí] (https://stackoverflow.com/q/12316406/465053) acerca de por qué doble control es aún necesario en primer lugar. – RBT

Respuesta

19

Sin volatile el código no funciona correctamente con varios hilos.

De Wikipedia de Double-checked locking:

A partir de J2SE 5.0, este problema se ha solucionado. La palabra clave volátil ahora garantiza que varios subprocesos manejen correctamente la instancia de singleton. Este nuevo lenguaje se describe en The "Double-Checked Locking is Broken" Declaration:

// Works with acquire/release semantics for volatile 
// Broken under Java 1.4 and earlier semantics for volatile 
class Foo { 
    private volatile Helper helper = null; 
    public Helper getHelper() { 
     Helper result = helper; 
     if (result == null) { 
      synchronized(this) { 
       result = helper; 
       if (result == null) { 
        helper = result = new Helper(); 
       } 
      } 
     } 
     return result; 
    } 

    // other functions and members... 
} 

En general se debe evitar una doble comprobación de bloqueo, si es posible, ya que es difícil hacerlo bien y si uno se equivoca, puede ser difícil encontrar el error . Prueba este enfoque más simple en su lugar:

Si el objeto ayudante es estática (una por el cargador de clases), una alternativa es la initialization on demand holder idiom

// Correct lazy initialization in Java 
@ThreadSafe 
class Foo { 
    private static class HelperHolder { 
     public static Helper helper = new Helper(); 
    } 

    public static Helper getHelper() { 
     return HelperHolder.helper; 
    } 
} 
+0

para Wikipedia –

+2

doble cierre comprobado puede ser necesario en determinadas situaciones donde se tiene un producto único que puede cambiar en tiempo de ejecución, pero pueden existir sólo una vez en su programa. es decir, una sesión de inicio de sesión, el que desea utilizar para acceder a un sitio, pero hay que volver a conectar y conseguir uno nuevo de vez en cuando. Si el valor no cambia durante el tiempo de ejecución sin embargo, se debe evitar – Xtroce

+0

¿Cómo va a pasar argumentos al constructor en el caso de la expresión idiomática titular? –

35

El volatile impide que las escrituras en memoria de ser reordenado, haciendo imposible que otros hilos lean campos no inicializados de su singleton a través del puntero de singleton.

Tenga en cuenta esta situación: el hilo A descubre que uniqueInstance == null, bloquea, confirma que todavía está null, y llama al constructor de singleton. El constructor realiza una escritura en el miembro XYZ dentro de Singleton y lo devuelve. El subproceso A ahora escribe la referencia al singleton recién creado en uniqueInstance, y se prepara para liberar su bloqueo.

Al igual que el hilo A se prepara para soltar su bloqueo, aparece el hilo B y descubre que uniqueInstance no es uniqueInstance, no es . El subproceso B tiene acceso a uniqueInstance.XYZ pensando que se ha inicializado, pero como la CPU ha reordenado las escrituras, los datos que el subproceso A ha escrito en XYZ no se han hecho visibles al subproceso B. Por lo tanto, el subproceso B ve un valor incorrecto dentro de XYZ, que es incorrecto.

Cuando se marca uniqueInstance volátil, una barrera de memoria se inserta. Todas las escrituras iniciadas antes de uniqueInstance se completarán antes de que se modifique el uniqueInstance, evitando la situación de reordenamiento descrita anteriormente.

+1

gracias por la respuesta –

+0

Para ser más específicos, las dos escrituras reordenados son: 1) Un asigna direcciones de memoria a 'uniqueInstance', y 2)' xyz consigue algo significativo. – lcn

+0

@dasblinkenlight: usted ha dicho que "debido a que la CPU ha reordenado escribe ....". Por favor, explique qué significa reordenamiento escrito? –

7

Para evitar el uso de doble cierre, o volátil que utiliza el seguimiento

enum Singleton { 
    INSTANCE; 
} 

Creación de la instancia es simple, perezoso cargado y seguro para subprocesos.

0

Escribir en un campo volátil sucederá antes de cualquier operación de lectura. A continuación se muestra un código de ejemplo para una mejor comprensión:

private static volatile ResourceService resourceInstance; 
//lazy Initialiaztion 
public static ResourceService getInstance() { 
    if (resourceInstance == null) { // first check 
     synchronized(ResourceService.class) { 
      if (resourceInstance == null) { // double check 
       // creating instance of ResourceService for only one time 
       resourceInstance = new ResourceService();      
      } 
     } 
    } 
    return resourceInstance; 
} 

Este enlace puede servirle mejor http://javarevisited.blogspot.com/2011/06/volatile-keyword-java-example-tutorial.html

1

Puede utilizar el código de seguimiento:

private static Singleton uniqueInstance; 

public static synchronized Singleton getInstance(){ 
    if(uniqueInstance == null){ 
     uniqueInstance = new Singleton(); 
    } 
    return uniqueInstance 
} 
Cuestiones relacionadas