concurrent - atomicinteger java
AtomicXXX.lazySet(…) en términos de sucede antes de los bordes (2)
¿Qué significa el método AtomicXXX.lazySet (valor) en términos de aristas pasadas, antes utilizadas, utilizadas en la mayoría de los razonamientos JMM? El javadocs es puro en él, y Sun bug 6275329 dice:
La semántica es que se garantiza que la escritura no se reordenará con ninguna escritura anterior, pero se puede reordenar con operaciones subsiguientes (o, de manera equivalente, podría no ser visible para otros subprocesos) hasta que ocurra alguna otra acción de escritura o sincronización volátil).
Pero esto no es un razonamiento sobre los bordes de la HB, por lo que me confunde. ¿Significa que la semántica lazySet () no se puede expresar en términos de bordes de HB?
ACTUALIZACIÓN : Intentaré concretar mi pregunta. Puedo usar el campo volátil ordinario en el siguiente escenario:
//thread 1: producer
...fill some data structure
myVolatileFlag = 1;
//thread 2: consumer
while(myVolatileFlag!=1){
//spin-wait
}
...use data structure...
En este escenario, el uso de la "estructura de datos" en el consumidor es correcto, ya que la marca de lectura-lectura volátil hace que el borde de HB, garantizando que todo lo que se escribe en la "estructura de datos" por el productor se complete y sea visible por el consumidor. ¿Pero qué sucede si usaré AtomicInteger.lazySet / get en lugar de la escritura / lectura volátil en este escenario?
//thread 1: producer
...fill some data structure
myAtomicFlag.lazySet(1);
//thread 2: consumer
while(myAtomicFlag.get()!=1){
//spin-wait
}
...use data structure...
¿Seguirá siendo correcto? ¿Todavía puedo realmente en la "estructura de datos" los valores de visibilidad en el hilo del consumidor?
No es una pregunta "del aire": he visto este método en el código de LMAX Disruptor exactamente en este escenario, y no entiendo cómo probar que es correcto ...
Basado en el Javadoc de Inseguro (el putOrderedInt se utiliza en el AtomicInteger.lazySet)
/**
* Version of {@link #putObjectVolatile(Object, long, Object)}
* that does not guarantee immediate visibility of the store to
* other threads. This method is generally only useful if the
* underlying field is a Java volatile (or if an array cell, one
* that is otherwise only accessed using volatile accesses).
*/
public native void putOrderedObject(Object o, long offset, Object x);
/** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)} */
public native void putOrderedInt(Object o, long offset, int x);
Los campos de respaldo en las clases AtomicXXX son volátiles. El lazySet parece escribir en estos campos como si no fueran volátiles, lo que eliminaría los bordes antes de lo que estaba esperando. Como se indica en su enlace, esto sería útil para anular los valores para ser elegibles para GC sin tener que incurrir en la escritura volátil.
Editar:
Esto es para responder a su actualización.
Si echa un vistazo a la cotización que proporcionó en el enlace, perderá todas las garantías de memoria que tenía con la escritura volátil.
El lazySet no se ordenará arriba de donde se está escribiendo, pero sin ninguna otra sincronización real, perderá la garantía de que el consumidor verá cualquier cambio que se haya escrito antes. Es perfectamente legal retrasar la escritura de myAtomicFlag y, por lo tanto, las escrituras anteriores hasta que se produzca alguna otra forma de sincronización.
Las operaciones lazySet
no crean aristas lazySet
y, por lo tanto, no se garantiza que sean visibles inmediatamente. Esta es una optimización de bajo nivel que tiene solo unos pocos casos de uso, que se encuentran principalmente en estructuras de datos concurrentes.
El ejemplo de recolección de basura de anular los punteros de la lista vinculada no tiene efectos secundarios visibles para el usuario. Se prefiere la anulación, de modo que si los nodos en la lista están en diferentes generaciones, no obligue a realizar una recopilación más costosa para descartar la cadena de enlaces. El uso de lazySet mantiene una semántica higiénica sin incurrir en gastos de escritura volátiles.
Otro ejemplo es el uso de campos volátiles protegidos por un bloqueo, como en ConcurrentHashMap
. Los campos son volátiles para permitir lecturas sin bloqueo, pero las escrituras deben realizarse bajo un bloqueo para garantizar una consistencia estricta. Como la cerradura garantiza el borde anterior a la liberación, una optimización es utilizar lazySet cuando se escribe en los campos y se borran todas las actualizaciones al desbloquear. Esto ayuda a mantener corta la sección crítica al evitar paradas innecesarias y tráfico de autobuses.
Si escribe una estructura de datos concurrente, lazySet
es un buen truco para tener en cuenta. Es una optimización de bajo nivel por lo que solo vale la pena considerarla cuando se ajusta el rendimiento.