Saltar al contenido

¿Cuál es la diferencia entre atómico / volátil / sincronizado?

Ya no tienes que buscar más por todo internet ya que estás al lugar justo, contamos con la respuesta que necesitas recibir y sin liarte.

Solución:

Está preguntando específicamente sobre cómo trabajar internamente, así que aquí estás:

Sin sincronizacion

private int counter;

public int getNextUniqueIndex() 
  return counter++; 

Básicamente lee el valor de la memoria, lo incrementa y lo devuelve a la memoria. Esto funciona en un solo hilo, pero hoy en día, en la era de los cachés de múltiples núcleos, múltiples CPU y múltiples niveles, no funcionará correctamente. En primer lugar, introduce la condición de carrera (varios hilos pueden leer el valor al mismo tiempo), pero también problemas de visibilidad. Es posible que el valor solo se almacene en “local“La memoria de la CPU (algo de caché) y no es visible para otras CPU / núcleos (y, por lo tanto, subprocesos). Es por eso que muchos se refieren a copia local de una variable en un hilo. Es muy inseguro. Considere este código de detención de hilos popular pero roto:

private boolean stopped;

public void run() 
    while(!stopped) 
        //do some work
    


public void pleaseStop() 
    stopped = true;

Agregar volatile para stopped variable y funciona bien, si cualquier otro hilo modifica stopped variable a través de pleaseStop() método, tiene la garantía de ver ese cambio inmediatamente en el hilo de trabajo while(!stopped) círculo. Por cierto, esta tampoco es una buena manera de interrumpir un hilo, consulte: Cómo detener un hilo que se está ejecutando para siempre sin ningún uso y Detener un hilo java específico.

AtomicInteger

private AtomicInteger counter = new AtomicInteger();

public int getNextUniqueIndex() 
  return counter.getAndIncrement();

los AtomicInteger La clase usa operaciones de CPU de bajo nivel CAS (comparar e intercambiar) (¡no se necesita sincronización!). Le permiten modificar una variable en particular solo si el valor actual es igual a otra cosa (y se devuelve correctamente). Entonces cuando ejecutas getAndIncrement() en realidad se ejecuta en un bucle (implementación real simplificada):

int current;
do 
  current = get();
 while(!compareAndSet(current, current + 1));

Básicamente: leer; intente almacenar el valor incrementado; si no tiene éxito (el valor ya no es igual a current), lea y vuelva a intentarlo. los compareAndSet() se implementa en código nativo (ensamblado).

volatile sin sincronizacion

private volatile int counter;

public int getNextUniqueIndex() 
  return counter++; 

Este código no es correcto. Soluciona el problema de visibilidad (volatile se asegura de que otros hilos puedan ver los cambios realizados en counter) pero todavía tiene una condición de carrera. Esto se ha explicado varias veces: el incremento previo / posterior no es atómico.

El único efecto secundario de volatile es “rubor“almacena en caché para que todas las demás partes vean la versión más reciente de los datos. Esto es demasiado estricto en la mayoría de las situaciones; es por eso que volatile no es el predeterminado.

volatile sin sincronización (2)

volatile int i = 0;
void incIBy5() 
  i += 5;

El mismo problema que el anterior, pero aún peor porque i no es private. La condición de carrera aún está presente. Por qué es un problema? Si, digamos, dos subprocesos ejecutan este código simultáneamente, la salida podría ser + 5 o + 10. Sin embargo, le garantizamos que verá el cambio.

Múltiples independientes synchronized

void incIBy5() 
  int temp;
  synchronized(i)  temp = i 
  synchronized(i)  i = temp + 5 

Sorpresa, este código también es incorrecto. De hecho, está completamente equivocado. En primer lugar, está sincronizando en i, que está a punto de cambiar (además, i es un elemento primitivo, así que supongo que estás sincronizando de forma temporal Integer creado a través de autoboxing …) Completamente defectuoso. También podrías escribir:

synchronized(new Object()) 
  //thread-safe, SRSLy?

No pueden entrar dos hilos al mismo synchronized cuadra con la misma cerradura. En este caso (y de manera similar en su código), el objeto de bloqueo cambia con cada ejecución, por lo que synchronized efectivamente no tiene ningún efecto.

Incluso si ha utilizado una variable final (o this) para la sincronización, el código sigue siendo incorrecto. Primero se pueden leer dos hilos i para temp sincrónicamente (teniendo el mismo valor localmente en temp), luego el primero asigna un nuevo valor a i (digamos, del 1 al 6) y el otro hace lo mismo (del 1 al 6).

La sincronización debe abarcar desde la lectura hasta la asignación de un valor. Su primera sincronización no tiene ningún efecto (leer un int es atómico) y el segundo también. En mi opinión, estas son las formas correctas:

void synchronized incIBy5() 
  i += 5 


void incIBy5() 
  synchronized(this) 
    i += 5 
  


void incIBy5() 
  synchronized(this) 
    int temp = i;
    i = temp + 5;
  

Declarar una variable como volátil significa que la modificación de su valor afecta inmediatamente al almacenamiento de memoria real de la variable. El compilador no puede optimizar ninguna referencia hecha a la variable. Esto garantiza que cuando un subproceso modifica la variable, todos los demás subprocesos ven el nuevo valor inmediatamente. (Esto no está garantizado para variables no volátiles).

Declarando un atómico variable garantiza que las operaciones realizadas en la variable se producen de forma atómica, es decir, que todos los subpasos de la operación se completan dentro del subproceso, se ejecutan y no son interrumpidos por otros subprocesos. Por ejemplo, una operación de incremento y prueba requiere que la variable se incremente y luego se compare con otro valor; una operación atómica garantiza que ambos pasos se completarán como si fueran una sola operación indivisible / ininterrumpida.

Sincronizando Todos los accesos a una variable permiten que solo un subproceso a la vez acceda a la variable y obliga a todos los demás subprocesos a esperar a que ese subproceso de acceso libere su acceso a la variable.

El acceso sincronizado es similar al acceso atómico, pero las operaciones atómicas generalmente se implementan en un nivel más bajo de programación. Además, es completamente posible sincronizar solo algunos accesos a una variable y permitir que otros accesos no estén sincronizados (por ejemplo, sincronizar todas las escrituras en una variable pero ninguna de las lecturas de ella).

La atomicidad, la sincronización y la volatilidad son independientes attributes, pero generalmente se usan en combinación para hacer cumplir la cooperación de subprocesos adecuada para acceder a las variables.

Apéndice(Abril de 2016)

El acceso sincronizado a una variable generalmente se implementa mediante un monitor o semáforo. Estos son de bajo nivel mutex (exclusión mutua) mecanismos que permiten que un hilo adquiera el control de una variable o bloque de código exclusivamente, obligando a todos los demás hilos a esperar si también intentan adquirir el mismo mutex. Una vez que el subproceso propietario libera el mutex, otro subproceso puede adquirir el mutex a su vez.

Apéndice(Julio de 2016)

La sincronización se produce en un objeto. Esto significa que llamar a un método sincronizado de una clase bloqueará el this objeto de la convocatoria. Los métodos sincronizados estáticos bloquearán Class objeto en sí.

Asimismo, ingresar a un bloque sincronizado requiere bloquear el this objeto del método.

Esto significa que un método (o bloque) sincronizado se puede ejecutar en varios subprocesos al mismo tiempo si se bloquean en diferente objetos, pero solo un subproceso puede ejecutar un método sincronizado (o bloque) a la vez para cualquier soltero objeto.

volátil:

volatile es una palabra clave. volatile Obliga a todos los subprocesos a obtener el último valor de la variable de la memoria principal en lugar de la caché. No se requiere bloqueo para acceder a variables volátiles. Todos los hilos pueden acceder al valor de la variable volátil al mismo tiempo.

Utilizando volatile variables reduce el riesgo de errores de coherencia de la memoria, porque cualquier escritura en una variable volátil establece una relación de suceder antes con las lecturas posteriores de esa misma variable.

Esto significa que los cambios a volatile las variables siempre son visibles para otros hilos. Además, también significa que cuando un hilo lee un volatile variable, no solo ve el último cambio en el volátil, sino también los efectos secundarios del código que provocó el cambio.

Cuándo usarlo: un hilo modifica los datos y otros hilos deben leer el último valor de datos. Otros hilos tomarán alguna acción, pero no actualizarán los datos..

AtomicXXX:

AtomicXXX Las clases admiten programación segura para subprocesos sin bloqueo en variables individuales. Estas AtomicXXX clases (como AtomicInteger) resuelve errores de inconsistencia de memoria / efectos secundarios de la modificación de variables volátiles, a las que se ha accedido en múltiples subprocesos.

Cuándo usarlo: varios subprocesos pueden leer y modificar datos.

sincronizado:

synchronized es una palabra clave que se utiliza para proteger un método o bloque de código. Hacer que el método esté sincronizado tiene dos efectos:

  1. Primero, no es posible para dos invocaciones de synchronized métodos en el mismo objeto para intercalar. Cuando un hilo está ejecutando un synchronized método para un objeto, todos los demás subprocesos que invocan synchronized métodos para el mismo bloque de objetos (suspender la ejecución) hasta que el primer hilo finalice con el objeto.

  2. En segundo lugar, cuando un synchronized sale del mtodo, establece automticamente una relacin de ocurre antes con cualquier invocación posterior de un synchronized método para el mismo objeto. Esto garantiza que los cambios en el estado del objeto sean visibles para todos los subprocesos.

Cuándo usarlo: varios subprocesos pueden leer y modificar datos. Su lógica empresarial no solo actualiza los datos, sino que también ejecuta operaciones atómicas

AtomicXXX es equivalente a volatile + synchronized aunque la implementación es diferente. AmtomicXXX se extiende volatile variables + compareAndSet métodos pero no utiliza la sincronización.

Preguntas relacionadas con SE:

Diferencia entre volátil y sincronizado en Java

Booleano volátil vs AtomicBoolean

Buenos artículos para leer: (El contenido anterior se tomó de estas páginas de documentación)

https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

https://docs.oracle.com/javase/tutorial/essential/concurrency/atomic.html

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/package-summary.html

Te mostramos las comentarios y valoraciones de los usuarios

Acuérdate de que tienes autorización de parafrasear si tropezaste tu disgusto justo a tiempo.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *