variable explicacion java volatile

explicacion - synchronized java



Volatile Vs Atomic (6)

Esta pregunta ya tiene una respuesta aquí:

Leo en algún lugar debajo de la línea.

La palabra clave volátil de Java no significa atómica, es un concepto erróneo común de que después de declarar la operación volátil, ++ será atómica, para que la operación sea atómica aún necesita garantizar el acceso exclusivo mediante el uso de synchronized método o bloque synchronized en Java.

Entonces, ¿qué pasará si dos hilos atacan una variable primitiva volatile al mismo tiempo?

¿Significa esto que quienquiera que la prenda, primero establecerá su valor? Y si, mientras tanto, aparece algún otro subproceso y lee el valor antiguo mientras el primer subproceso cambiaba su valor, entonces ¿el nuevo subproceso no leerá su valor anterior?

¿Cuál es la diferencia entre Atomic y la palabra clave volátil?


Entonces, ¿qué pasará si dos hilos atacan una variable primitiva volátil al mismo tiempo?

Por lo general, cada uno puede incrementar el valor. Sin embargo, en algún momento, ambos actualizarán el valor al mismo tiempo y, en lugar de incrementar en un total de 2, ambos suben el incremento en 1 y solo se agrega 1.

¿Significa esto que quienquiera que la prenda, primero establecerá su valor?

No hay bloqueo Para eso está synchronized .

Y si entre tanto, aparece algún otro subproceso y lee el valor antiguo mientras el primer subproceso cambiaba su valor, entonces ¿el nuevo subproceso no leerá su valor anterior?

Sí,

¿Cuál es la diferencia entre Atomic y la palabra clave volátil?

AtomicXxxx envuelve un elemento volátil por lo que son básicamente los mismos, la diferencia es que proporciona operaciones de nivel superior como CompareAndSwap que se utiliza para implementar el incremento.

AtomicXxxx también es compatible con lazySet. Esto es como un conjunto volátil, pero no detiene el proceso esperando a que se complete la escritura. Puede significar que si lee un valor que acaba de escribir, es posible que vea el valor anterior, pero no debería hacerlo de todos modos. La diferencia es que configurar un valor volátil toma alrededor de 5 ns, bit lazySet toma alrededor de 0,5 ns.


Como Trying como se indica, volatile solo se ocupa de la visibilidad.

Considere este fragmento en un entorno concurrente:

boolean isStopped = false; : : while (!isStopped) { // do some kind of work }

La idea aquí es que algún hilo podría cambiar el valor de isStopped de falso a verdadero para indicar al ciclo siguiente que es hora de detener el bucle.

Intuitivamente, no hay problema. Lógicamente, si otro hilo hace que isStopped sea ​​igual a verdadero, entonces el ciclo debe terminar. La realidad es que el ciclo probablemente nunca terminará aunque otro hilo haga que isStopped sea ​​igual a verdadero.

La razón para esto no es intuitiva, pero considere que los procesadores modernos tienen múltiples núcleos y que cada núcleo tiene múltiples registros y múltiples niveles de memoria caché que no son accesibles para otros procesadores . En otras palabras, los valores que se almacenan en caché en la memoria local de un procesador no son visibles para los hilos que se ejecutan en un procesador diferente. Aquí radica uno de los problemas centrales de concurrencia: visibilidad.

El Modelo de memoria Java no garantiza en absoluto cuándo los cambios que se realizan en una variable en el hilo pueden ser visibles para otros hilos. Para garantizar que las actualizaciones sean visibles tan pronto como estén hechas, debe sincronizar.

La palabra clave volatile es una forma débil de sincronización. Si bien no hace nada por la exclusión mutua o la atomicidad, proporciona una garantía de que los cambios realizados en una variable en un subproceso serán visibles para otros subprocesos tan pronto como se realice. Debido a que las lecturas y escrituras individuales para variables que no son de 8 bytes son atómicas en Java, declarar las variables volatile proporciona un mecanismo fácil para proporcionar visibilidad en situaciones donde no existen otros requisitos de atomicidad o exclusión mutua.


El efecto de la palabra clave volatile es aproximadamente que cada operación individual de lectura o escritura en esa variable es atómica.

Notablemente, sin embargo, una operación que requiere más de una lectura / escritura, como i++ , que es equivalente a i = i + 1 , que hace una lectura y una escritura, no es atómica, ya que otro hilo puede escribirle a entre la lectura y la escritura.

Las clases Atomic , como AtomicInteger y AtomicReference , proporcionan una variedad más amplia de operaciones de forma atómica, incluyendo específicamente el incremento para AtomicInteger .


Hay dos conceptos importantes en el entorno de multihilo.

  1. atomicidad
  2. visibilidad

Volatile erradica el problema de visibilidad pero no se ocupa de la atomicidad. Volatile evitará que el compilador reordene la instrucción que implica la escritura y la posterior lectura de una variable volátil. por ejemplo, k++ Aquí k++ no es una instrucción de máquina individual, sino tres instrucciones de la máquina.

  1. copiar el valor para registrarse
  2. incrementarlo
  3. colocarlo de nuevo

Por lo tanto, aunque declare variable a volatile , no hará que esta operación sea atómica, lo que significa que otro subproceso puede ver un resultado intermedio que es un valor obsoleto o no deseado para el otro subproceso.

Pero AtomicInteger , AtomicReference se basa en las instrucciones Compare y Swap . CAS tiene tres operandos: una ubicación de memoria V sobre la cual operar, el valor antiguo esperado A y el nuevo valor B CAS actualiza atómicamente V al nuevo valor B , pero solo si el valor en V coincide con el valor A esperado anterior; de lo contrario, no hace nada. En cualquier caso, devuelve el valor actualmente en V Esto es utilizado por JVM en AtomicInteger , AtomicReference y llaman a la función como compareAndSet() . Si el procesador subyacente no admite esta funcionalidad, JVM la implementa mediante bloqueo por rotación .


La palabra clave volatile se usa:

  • para hacer que las operaciones no atómicas de 64 bits sean atómicas: long y double . (todos los demás accesos primitivos ya están garantizados como atómicos)
  • hacer actualizaciones variables garantizadas para ser vistas por otros hilos + efectos de visibilidad: después de escribir en una variable volátil, todas las variables visibles antes de escribir esa variable se vuelven visibles para otro hilo después de leer la misma variable volátil (suceder antes del pedido).

Las clases java.util.concurrent.atomic.* Son, de acuerdo con los documentos java :

Un pequeño conjunto de herramientas de clases que admiten la programación sin bloqueo de subprocesos en variables individuales. En esencia, las clases de este paquete extienden la noción de valores volátiles, campos y elementos de matriz a aquellos que también proporcionan una operación de actualización condicional atómica del formulario:

boolean compareAndSet(expectedValue, updateValue);

Las clases atómicas se basan en la función atómica compareAndSet(...) que se asigna a una instrucción de CPU atómica. Las clases atómicas introducen lo que sucede antes de ordenar como lo hacen las variables volatile . (con una excepción: weakCompareAndSet(...) ).

De los documentos de java:

Cuando un hilo ve una actualización de una variable atómica causada por un weakCompareAndSet, no necesariamente ve actualizaciones a otras variables que ocurrieron antes del weakCompareAndSet.

Para su pregunta:

¿Significa esto que quienquiera que la prenda, primero establecerá su valor? Y si entre tanto, aparece algún otro subproceso y lee el valor antiguo mientras el primer subproceso cambiaba su valor, entonces ¿el nuevo subproceso no leerá su valor anterior?

No bloquea nada, lo que está describiendo es una condición de carrera típica que sucederá eventualmente si los subprocesos acceden a los datos compartidos sin una sincronización adecuada. Como ya se mencionó, declarar una variable volatile en este caso solo asegurará que otros subprocesos verán el cambio de la variable (el valor no se almacenará en caché en un registro de alguna memoria caché que solo se ve en una cadena).

¿Cuál es la diferencia entre AtomicInteger y volatile int ?

AtomicInteger proporciona operaciones atómicas en un int con una sincronización adecuada (por ejemplo, incrementAndGet() , getAndAdd(...) , ...), volatile int solo asegurará la visibilidad del int a otros hilos.


Volátil y atómico son dos conceptos diferentes. Volátil asegura que cierto estado esperado (de memoria) es verdadero en diferentes subprocesos, mientras que Atomics asegura que el funcionamiento en variables se realiza atómicamente.

Tome el siguiente ejemplo de dos hilos en Java:

Tema A:

value = 1; done = true;

Hilo B:

if (done) System.out.println(value);

Comenzando con value = 0 y done = false la regla de threading nos dice que no está definido si el Thread B imprimirá o no el valor. ¡Además, el valor no está definido en ese punto también! Para explicar esto, necesita saber un poco sobre la gestión de memoria Java (que puede ser compleja), en resumen: los subprocesos pueden crear copias locales de variables, y la JVM puede reordenar el código para optimizarlo, por lo tanto, no hay garantía de que el código anterior se ejecuta exactamente en ese orden. El ajuste hecho a verdadero y luego establecer el valor en 1 podría ser un posible resultado de las optimizaciones de JIT.

volatile solo garantiza que, en el momento del acceso de dicha variable, el nuevo valor será inmediatamente visible para todos los demás subprocesos y el orden de ejecución garantiza que el código se encuentre en el estado que cabría esperar. Entonces, en el caso del código anterior, definir done como volátil asegurará que cada vez que el Tema B verifique la variable, sea falso o verdadero, y si es verdadero, el value se ha establecido en 1.

Como efecto secundario de la volatilidad , el valor de dicha variable se establece de manera atómica en todo el subproceso (a un costo muy bajo de la velocidad de ejecución). Sin embargo, esto solo es importante en los sistemas de 32 bits que utilizan variables largas (de 64 bits) (o similares), en la mayoría de los casos la configuración / lectura de una variable es atómica. Pero hay una diferencia importante entre un acceso atómico y una operación atómica. Volátil solo asegura que el acceso es atómico, mientras que Atomics garantiza que la operación sea ​​atómica.

Toma el siguiente ejemplo:

i = i + 1;

No importa cómo defina i, un subproceso diferente leyendo el valor justo cuando se ejecuta la línea anterior podría obtener i, o i + 1, porque la operación no es atómica. Si el otro subproceso establece i en un valor diferente, en el peor de los casos, podría volver a establecerlo como antes por el subproceso A, porque estaba justo en el medio de calcular i + 1 en función del valor anterior, y luego establecer i de nuevo a ese viejo valor + 1. Explicación:

Assume i = 0 Thread A reads i, calculates i+1, which is 1 Thread B sets i to 1000 and returns Thread A now sets i to the result of the operation, which is i = 1

Atomics como AtomicInteger aseguran que tales operaciones ocurren atómicamente. Entonces, el problema anterior no puede suceder, yo sería 1000 o 1001 una vez que ambos hilos hayan terminado.