2012-09-14 10 views
10
código

muestra:¿El bloqueo explícito proporciona automáticamente visibilidad de la memoria?

class Sample{ 
    private int v; 
    public void setV(){ 
     Lock a=new Lock(); 
     a.lock(); 
     try{ 
      v=1; 
     }finally{ 
      a.unlock(); 
     } 
    } 
    public int getV(){ 
     return v; 
    } 
} 

Si tengo un hilo invoca constantemente GETV y acabo de hacer SETV vez en otro hilo, ¿Es ese hilo lectura garantizada para ver el nuevo valor justo después de la escritura? ¿O necesito hacer "V" volátil o AtomicReference?

Si la respuesta es no, entonces debo cambiar en:

class Sample{ 
    private int v; 
    private Lock a=new Lock(); 
    public void setV(){ 
     a.lock(); 
     try{ 
      v=1; 
     }finally{ 
      a.unlock(); 
     } 
    } 
    public int getV(){ 
     a.lock(); 
     try{ 
      int r=v; 
     }finally{ 
      a.unlock(); 
     } 
     return r; 
    } 
} 
+0

¿Quieres decir 'ReentrantLock'? –

+0

Sí. Pero no solo ReentrantLock sino también ISchedulingRule e ILock del framework eclipse RCP. –

Respuesta

7

Desde el documentation:

implementaciones de todos los seguros debe hacer cumplir la misma semántica de sincronización de memoria a lo dispuesto por la incorporada en el bloqueo del monitor:

  • Una operación de bloqueo exitoso actúa como una acción MonitorEnter éxito
  • Una operación de desbloqueo exitosa actúa como una acción de salida de monitor exitosa

Si usa Locken ambos hilos (es decir la lectura y la escritura), el hilo de lectura verá el nuevo valor, porque monitorEnter vacía la caché. De lo contrario, debe declarar la variable volatile para forzar una lectura de la memoria en el hilo de lectura.

+0

¿Puedo pedir un poco más? ¿Puede explicar un poco más acerca de "vaciar el caché". ¿Eso significa que no importa qué subproceso ingrese en un monitor, todos los procesadores descargarán su caché? ¿O solo se borrarán los datos del caché relacionados con el monitor ingresado? –

+0

@TempleWing Tengo entendido que JVM vacía el caché antes de tratar de ingresar a un monitor, así que sí, todos los subprocesos contendientes vacían sus cachés en la memoria y luego ingresan el bloqueo uno a uno. Por supuesto, si una CPU no ejecuta un hilo que intenta ingresar un bloqueo, ese caché de CPU no se vaciará. – dasblinkenlight

0

Debe hacer volátil o un AtomicInteger. Esto asegurará que el hilo de lectura tarde o temprano vea el cambio, y lo suficientemente cerca como para "justo después" para la mayoría de los propósitos. Y técnicamente no necesitas el bloqueo para una simple actualización atómica como esta. Eche un vistazo de cerca a la API de AtomicInteger. set(), compareAndSet(), etc ... establecerán el valor para que sea visible leyendo hilos de manera atómica.

1

De acuerdo con la ley de Brian ...

Si está escribiendo una variable que el próximo podría ser leído por otra hilo, o la lectura de una variable que podría haber sido escrito por última otro hilo, debe use la sincronización, y además, tanto el lector como el escritor deben sincronizarse usando el mismo bloqueo del monitor.

Por lo tanto, sería apropiado para sincronizar tanto el setter y getter ......

O

Uso AtomicInteger.incrementAndGet() lugar si se quiere evitar el bloquelock-unlock (es decir, bloque sincronizado)

0

bloqueos explícitos, synchronized, referencia atómica y volatile, todos proporcionan visibilidad de la memoria. Lock y synchronized lo hacen para el bloque de código que rodean y referencia atómica y volatile para la variable particular declarada así. Sin embargo, para que la visibilidad funcione correctamente, tanto el método de lectura como el de escritura deberían estar protegidos por el mismo objeto de bloqueo.

No funcionará en su caso porque su método getter no es proyectado por la cerradura que protege el método setter. Si realiza el cambio, funcionará según sea necesario. También solo declarará la variable como volatile o AtomicInteger o AtomicReference<Integer>.

1

Si tengo un hilo invoca constantemente GETV y acabo de hacer una vez en SETV otro hilo, es que la lectura de hilo garantizado para ver el nuevo valor justo después de la escritura?

NO, el hilo de la lectura puede simplemente leer su propia copia (en caché de forma automática por el núcleo de la CPU, que el hilo se está ejecutando en la lectura) del valor v 's, y por lo tanto no obtener el valor más reciente.

¿O tengo que hacer que "V" sea volátil o AtomicReference?

SÍ, ambos funcionan.

Haciendo v volátil simplemente detener núcleo de la CPU del valor de almacenamiento en caché v 's, es decir, cada operación de lectura/escritura a la variable v deben tener acceso a la memoria principal, que es más lento (alrededor de 100x veces más lento que leer de caché L1, ver interaction_latency de detalles)

El uso de v = new AtomicInteger() funciona porque AtomicInteger usa un private volatile int value; internamente para proporcionar visibilidad.

Y, también funciona si se utiliza bloqueo (si un objeto Lock o utilizar synchronized bloque o método) en la lectura y el hilo de escritura (como su segundo segmento de código hace), porque (según la sección Second Edition of The Java ® Virtual Machine Specification 8.9)

... bloqueo de cualquier cerradura vuelca conceptualmente todas las variables de la memoria de trabajo de un hilo , y desbloquear cualquier bloqueo fuerza la escritura a la memoria principal de todas las variables que el mensaje ha asignado ...

.. .Si un subproceso usa una variable compartida particular o después de bloquear un bloqueo particular y antes del desbloqueo correspondiente del mismo bloqueo , el hilo leerá el valor compartido de esa variable desde la memoria principal después de la operación de bloqueo, si es necesario, y copiará a la memoria principal. valor asignado más recientemente a esa variable antes de la operación de desbloqueo.Esto, en conjunto con las reglas de exclusión mutuos para las cerraduras, es suficiente para garantizar que los valores son transmitido correctamente desde un hilo a otro a través de las variables compartidas ...

P. S. AtomicXXX también proporcionan operaciones CAS (Compare and Swap) que es útil para el acceso mutththread.

P.P.S. La especificación jvm sobre este tema no ha cambiado desde Java 6, por lo que no están incluidas en jvm specification for java 7, 8, and 9.

P.P.P.S. De acuerdo con this article, las memorias caché de la CPU son siempre coherentes, independientemente de la vista de cada núcleo. La situación en su pregunta es causada por los 'Búferes de orden de la memoria', en los que las instrucciones store & load (que se utilizan para escribir y leer datos de la memoria, en consecuencia) podrían reordenarse por rendimiento. En detalle, el búfer permite una instrucción load para adelantarse a una instrucción anterior store, que causa exactamente el problema. Sin embargo, en mi opinión, esto es más difícil de entender, por lo que 'caché para diferentes núcleos (como lo hicieron las especificaciones de JVM)' podría ser un mejor modelo conceptual.

+0

verifique esta pregunta para mayor discusión: https://stackoverflow.com/questions/1850270/memory-effects-of-synchronization-in-java –

Cuestiones relacionadas