Domine el arte de los subprocesos múltiples: aprenda a usar la palabra clave volátil en Java para mejorar el rendimiento y simplificar la codificación

Jan 16 2023
Una breve mirada a lo que sucede detrás de escena cuando usamos la palabra clave volatile en Java y por qué no es un reemplazo completo para la sincronización a través de la palabra clave volátil. Esta palabra clave se puede usar para asegurarse de que cualquier cambio en una variable sea inmediatamente visible para otros subprocesos, lo que ayuda a evitar que nuestros datos se vuelvan obsoletos, desincronizados e incorrectos.

Una breve mirada a lo que sucede detrás de escena cuando usamos la volatilepalabra clave en Java y por qué no es un reemplazo completo para la sincronización .

Foto de Christin Hume en Unsplash

Al profundizar en el turbio mundo de la concurrencia y las aplicaciones de subprocesos múltiples dentro de Java, es probable que se encuentre con la volatilepalabra clave.

Esta palabra clave se puede usar para asegurarse de que cualquier cambio en una variable sea inmediatamente visible para otros subprocesos, lo que ayuda a evitar que nuestros datos se vuelvan obsoletos, desincronizados e incorrectos.

Cuando profundice en cualquier documentación sobre esta palabra clave, aprenderá que funciona asegurándose de que una variable se lea directamente desde la memoria y no desde un caché. Eso es genial... pero ¿qué significa realmente?

¿Cómo funciona la volatilepalabra clave debajo del capó?

En una aplicación de subprocesos múltiples, es posible que cada subproceso se ejecute a través de una CPU separada.

Al trabajar con variables, cada subproceso podría hacer una copia del valor de una variable de la memoria principal y almacenarlo en su propio caché de CPU para mejorar el rendimiento.

Esto garantiza que cuando el subproceso necesite acceder al valor de una variable, se pueda leer desde la memoria caché de la CPU y no necesite acceder a la memoria principal, lo que ahorra tiempo.

Múltiples CPU, cada una con su propio caché

En el ejemplo anterior, myVarno es volátil y, por lo tanto, los datos pueden perder la sincronización dependiendo de cuándo la máquina virtual Java (JVM) lee o escribe datos hacia y desde cualquiera de los cachés de la CPU a la memoria principal.

Por ejemplo, si uno de los subprocesos actualiza el myVarvalor a 5, no hay garantía de cuándo myVarse escribe el valor de la memoria caché de la CPU en la memoria principal.

Esto significa que el myVarvalor en una de las memorias caché de la CPU puede no ser el mismo que en la memoria principal, lo que genera valores diferentes entre subprocesos.

Aquí uno de los cachés de la CPU no está sincronizado con los otros cachés y la memoria principal

Haciendo la variablevolatiley por lo tanto siempre leyendo y escribiendo el valor de la memoria principal, a diferencia de un caché de la CPU, este problema se puede evitar.

¿Qué significa esto para su código?

Por lo tanto, podemos utilizar elvolatilepalabra clave en nuestro código para asegurarnos de que siempre leemos nuestros valores desde la memoria principal y no desde un caché de CPU específico.

En la clase a continuación, myVarno es volatile. Si tenemos 1 subproceso que lee y actualiza myVar, mientras que varios otros subprocesos están leyendo desde myVar, tarde o temprano se desincronizará en las memorias caché de la CPU, como se muestra en los diagramas anteriores.

public class MyNonThreadSafeCode {
  public int myVar = 1;
}

public class MyThreadSafeCode {
  public volatile int myVar = 1;
}

Beneficios de usar volátiles

Entonces, ¿cuándo deberíamos usar volatile en nuestro código? ¿Qué ventajas ofrece? He esbozado algunos a continuación:

Visibilidad entre hilos

Como se ha explicado anteriormente, la volatilepalabra clave garantiza que cualquier cambio realizado en una variable volátil será inmediatamente visible para otros subprocesos. Esto ayuda a evitar el problema de los datos obsoletos, donde un subproceso puede tener una copia en caché del valor de una variable que no es el más actualizado.

Subprocesamiento múltiple simplificado

El uso de variables volátiles puede simplificar los subprocesos múltiples al permitir que los subprocesos se comuniquen entre sí sin necesidad de bloqueos u otros mecanismos de sincronización . Esto puede eliminar la necesidad de un código de bloqueo complejo y, a menudo, propenso a errores. También siento que mantiene el código más limpio y más fácil de leer que los bloqueos o synchronizedbloques, cuando se usa correctamente.

Rendimiento mejorado (en algunos casos)

En ciertos casos, el uso de variables volátiles puede mejorar el rendimiento, ya que reduce la necesidad de bloqueos. Sin embargo, el uso de la palabra clave volátil en realidad puede tener un impacto negativo en el rendimiento si se usa incorrectamente. He discutido cómo y por qué en la sección "Inconvenientes y consideraciones al usar la palabra clave volátil" a continuación.

Útil para la señalización

Las variables volátiles se pueden utilizar como un mecanismo de señalización simple entre subprocesos. Por ejemplo, un subproceso puede esperar a que una variable volátil cambie su valor, lo que indica que otro subproceso ha completado una tarea específica.

Inconvenientes y consideraciones al usar la palabra clave volátil

Todo eso suena bastante bien, pero volatileno es una panacea. Solo debe usarse cuando sea necesario y no es un reemplazo directo para la sincronización (por ejemplo, a través de la synchronizedpalabra clave) en su código. Este es el por qué:

Inconvenientes de rendimiento cuando se usa incorrectamente

Debido a que la volatilepalabra clave significa que el valor de su variable se leerá directamente desde la memoria principal, tendrá un impacto negativo en el rendimiento.

Por lo tanto, la volatilepalabra clave solo debe usarse cuando una variable no es inmutable (es decir, se va a cambiar) y se va a usar en un entorno concurrente (es decir, será accedida por más de un subproceso) para no entorpecer el rendimiento de su aplicación.

No apto para operaciones de lectura, modificación y escritura en varios subprocesos

Como puede ver en los ejemplos anteriores, no se produce ningún bloqueo cuando accedemos a una volatilevariable. Por lo tanto, a diferencia de otros métodos para hacer que el código sea seguro para subprocesos, como la synchronizedpalabra clave, la palabra clave volátil no debe usarse en casos en los que queremos realizar una operación de "lectura-modificación-escritura".

Por ejemplo, si tenemos una aplicación de contador, un caso de uso típico sería implementar un método de incremento. Este método leerá el valor actual del contador, lo modificará sumando 1 al valor actual y luego volverá a escribir el nuevo valor en el contador.

Cada una de estas operaciones puede no ocurrir en una sola instrucción/ciclo de CPU. Por lo tanto, es posible que otro hilo pueda interferir, accediendo a la variable de contador entre las operaciones de lectura y escritura del hilo original. Por lo tanto, se puede perder fácilmente un incremento en el contador.

P.ej

Thread 1lee el valor de countercomo 0 de la memoria principal

Thread 1realiza el cálculo para modificar el valor de countera 1

Thread 2lee el valor de countercomo 0 de la memoria principal

Thread 1escribe el valor de countercomo 1 en la memoria principal

Thread 2realiza el cálculo para modificar el valor de countera 1

Thread 2escribe el valor de countercomo 1 en la memoria principal

En resumen, la volatilepalabra clave solo garantiza que cualquier cambio en una variable volátil será inmediatamente visible para otros subprocesos. No garantiza que los cambios realizados por un subproceso se completen antes de que otro subproceso pueda ver el valor actualizado.

La sincronización, por otro lado, proporciona garantías más sólidas para la consistencia de la memoria mediante el uso de bloqueos para garantizar que solo un subproceso pueda ejecutar una sección específica de código a la vez. Esto garantiza que cualquier cambio realizado por un subproceso se complete antes de que otro subproceso pueda ver el valor actualizado.

Solo se aplica a las variables.

volatilesólo se aplica a las variables, mientras que elsynchronizedLa palabra clave se puede aplicar a métodos completos y bloques de código. Por lo tanto, es importante considerar qué método funciona mejor para su caso de uso particular.

Muchas gracias por tomarse el tiempo de leer esto. Esperamos que haya sido de alguna ayuda para comprender cómo funciona realmente la palabra clave volátil y cuándo usarla.

Si disfrutó esto o lo encontró informativo, considere leer algunos de mis otros artículos o seguirme en Medium. ¡Salud!

© Copyright 2021 - 2023 | unogogo.com | All Rights Reserved