2011-11-05 14 views
8

que tienen una porción de código que se parece a esto:¿Las variables volátiles de Java imponen una relación de pasar antes de que se lea?

Fragmento A:

class Creature { 
    private static long numCreated; 
    public Creature() { 
     synchronized (Creature.class) { 
      numCreated++; 
     } 
    } 
    public static long numCreated() { 
     return numCreated; 
    } 
} 

Desde mi entender, ya que la lectura de numCreated no está sincronizado, si Rosca-A crea un Creature a la 1 pm , y el hilo B lee numCreated() a las 2 p.m., numCreated() bien puede haber devuelto 0 o 1 (incluso cuando el hilo-A ha terminado de inicializar el objeto a las 1.05 p.m.).

por lo que añade synchronized a numCreated():

fragmento B:

class Creature { 
    private static long numCreated; 
    public Creature() { 
     synchronized (Creature.class) { 
      numCreated++; 
     } 
    } 
    public static synchronized long numCreated() { // add "synchronized" 
     return numCreated; 
    } 
} 

y todo está bien, excepto que yo estaba pensando, si modifico a Fragmento C, es la variable numCreated ¿Todavía está sincronizado correctamente?

Fragmento C:

class Creature { 
    private static volatile long numCreated; // add "volatile" 
    public Creature() { 
     synchronized (Creature.class) { 
      numCreated++; 
     } 
    } 
    public static long numCreated() { // remove "synchronized" 
     return numCreated; 
    } 
} 

Con Fragmento C, es que garantiza que tan pronto como hilo-A completa la creación de objetos a las 1:05 de la tarde, el llamado del Thread-B a numCreated() es seguro para volver 1?

PS: Tengo entendido que en una situación real que probablemente utilice un AtomicLong pero esto es con fines de aprendizaje

+0

lecturas volátiles son baratas, esto es lo que necesita saber. Sin embargo, las escrituras no son baratas, sin embargo. La sincronización requiere tanto una escritura volátil como CAS (comparar y establecer), además de que los monitores desafiados se lanzan al sistema operativo, lo que es la motivación para impl bloquearse. – bestsss

+0

dado el comentario anterior, no intente jugar * más inteligente * quitando volátil. – bestsss

+0

@bestsss No entiendo tu último comentario, ¿a qué te refieres con no eliminar la volatilidad? (en Snippet C agregué volátiles y eliminé el sincronizado) – Pacerier

Respuesta

6

Ver http://download.oracle.com/javase/6/docs/api/java/util/concurrent/package-summary.html#MemoryVisibility:

una escritura en un campo volátil sucede-antes de cada lectura posterior de ese mismo campo. Las escrituras y lecturas de campos volátiles tienen efectos de coherencia de memoria similares a los de como entrar y salir de monitores, pero no implican el bloqueo de exclusión mutua.

Así que la respuesta es sí. La escritura del volátil en el constructor ocurre antes de la lectura del volátil en numCreated(). Y dado que el incremento no atómico aún se realiza en un bloque sincronizado, la sincronización es correcta (el incremento no es atómico, pero la escritura de la longitud volátil es).

+0

Heys gracias por la ayuda = D Por cierto, ¿podría comentar sobre el rendimiento del uso de volátiles (Snippet C) en comparación con el rendimiento de usar sincronizado para la lectura (Fragmento B)? En otras palabras, ¿preferirías B o C? – Pacerier

+0

Preferiría un AtomicLong, porque facilita la sincronización y el punto es obvio. Sin ATomicLong, preferiría el fragmento B porque es más fácil de entender y no mezcla dos estrategias de sincronización. La diferencia de rendimiento debería ser insignificante en el 99% de los casos de todos modos. El multi-threading es muy difícil, no suele ser abordado por la mayoría de los desarrolladores, y no es muy conocido. De este modo, me importaría la legibilidad y la corrección antes de preocuparme por el rendimiento. –

+0

El rendimiento de ambos es similar desde la corrección del modelo de memoria java (¿versión?) –

Cuestiones relacionadas