usar new icon example codigo borderfactory java multithreading concurrency volatile

java - new - ¿Hay algún punto en el uso de un largo volátil?



new icon java (3)

Ocasionalmente uso una variable de instancia volatile en los casos en que tengo dos subprocesos que leen / escriben y no quiero la sobrecarga (o el posible riesgo de bloqueo) de sacar un bloqueo; por ejemplo, un subproceso de temporizador que actualiza periódicamente un ID int que se expone como un captador en alguna clase:

public class MyClass { private volatile int id; public MyClass() { ScheduledExecutorService execService = Executors.newScheduledThreadPool(1); execService.scheduleAtFixedRate(new Runnable() { public void run() { ++id; } }, 0L, 30L, TimeUnit.SECONDS); } public int getId() { return id; } }

Mi pregunta: dado que el JLS solo garantiza que las lecturas de 32 bits serán atómicas, ¿tiene sentido alguna vez usar un volátil largo? (es decir, 64 bits).

Advertencia : Por favor, no responda diciendo que el uso de volatile sobre la synchronized es un caso de optimización previa; Soy muy consciente de cómo / cuándo usar la synchronized pero hay casos en volatile es preferible la volatile . Por ejemplo, al definir un bean Spring para su uso en una aplicación de un solo hilo, tiendo a preferir variables de instancia volatile , ya que no hay garantía de que el contexto Spring inicialice las propiedades de cada bean en el thread principal.


"volátil" sirve para múltiples propósitos:

  • Garantiza escrituras atómicas a doble / largo.
  • garantiza que cuando un subproceso A ve un cambio en la variable volátil producida por el subproceso B, el subproceso A también puede ver todos los demás cambios realizados por el subproceso B antes del cambio a la variable volátil (piense en establecer el número de celdas utilizadas en la matriz después de configurar las celdas) .
  • evita la optimización del compilador basándose en el supuesto de que solo un hilo puede cambiar la variable (piense en un ciclo apretado while while (l != 0) {} .

¿Hay más?


Esto se puede demostrar con el ejemplo.

  • alternar constantemente dos campos, uno marcado como volátil y uno no entre todos los bits establecidos y todos los bits desactivados
  • leer los valores de campo en otro hilo
  • ver que el campo foo (no protegido con volatile) se puede leer en un estado inconsistente, esto nunca sucede en el campo de barra protegido con volatile

Código

public class VolatileTest { private long foo; private volatile long bar; private static final long A = 0xffffffffffffffffl; private static final long B = 0; private int clock; public VolatileTest() { new Thread(new Runnable() { @Override public void run() { while (true) { foo = clock % 2 == 0 ? A : B; bar = clock % 2 == 0 ? A : B; clock++; } } }).start(); while (true) { long fooRead = foo; if (fooRead != A && fooRead != B) { System.err.println("foo incomplete write " + Long.toHexString(fooRead)); } long barRead = bar; if (barRead != A && barRead != B) { System.err.println("bar incomplete write " + Long.toHexString(barRead)); } } } public static void main(String[] args) { new VolatileTest(); } }

Salida

foo incomplete write ffffffff00000000 foo incomplete write ffffffff00000000 foo incomplete write ffffffff foo incomplete write ffffffff00000000

Tenga en cuenta que esto solo sucede cuando ejecuto en una máquina virtual de 32 bits, en una máquina virtual de 64 bits no pude obtener un solo error en varios minutos.


No estoy seguro si entiendo su pregunta correctamente, pero el JLS 8.3.1.4. estados de campos volátiles :

Un campo puede ser declarado volátil, en cuyo caso el modelo de memoria Java garantiza que todos los subprocesos ven un valor consistente para la variable ( §17.4 ).

y, quizás más importante, el tratamiento no atómico JLS 17.7 de doble y largo :

17.7 Tratamiento no atómico de doble y largo.
[...]
Para los fines del modelo de memoria del lenguaje de programación Java, una sola escritura en un valor largo o doble no volátil se trata como dos escrituras separadas: una para cada mitad de 32 bits. Esto puede dar lugar a una situación en la que un hilo ve los primeros 32 bits de un valor de 64 bits de una escritura y los segundos 32 bits de otra escritura. Las escrituras y lecturas de valores volátiles largos y dobles son siempre atómicas. Las escrituras y lecturas de referencias son siempre atómicas, independientemente de si se implementan como valores de 32 o 64 bits.

Es decir, la variable "completa" está protegida por el modificador volátil, no solo las dos partes. Esto me tienta a afirmar que es aún más importante usar volatile durante long s de lo que es para int s, ya que ni siquiera una lectura es atómica para largos / dobles no volátiles.