secondary - sincronización de hilos java
replicar mongodb (8)
Dado que es un método de solo lectura. Debes sincronizar el método set
.
EDITAR : veo por qué el método get también debe sincronizarse. Buen trabajo explicando a Phil Ross.
En la clase siguiente, ¿es seguro el subproceso getIt()
y por qué?
public class X {
private long myVar;
public void setIt(long var){
myVar = var;
}
public long getIt() {
return myVar;
}
}
Debería serlo, y generalmente lo es, pero no está garantizado que sea seguro para subprocesos. Puede haber problemas con diferentes núcleos que tienen diferentes versiones en la memoria caché de la CPU, o la tienda / recuperación no es atómica para todas las arquitecturas. Usa la clase AtomicLong
.
Esto no es seguro para subprocesos. Incluso si su plataforma garantiza escrituras atómicas de long
, la falta de synchronized
hace posible que un hilo invoque a setIt()
e incluso después de que esta llamada haya terminado, es posible que otro hilo pueda llamar a getIt()
y esta llamada pueda devolver el valor anterior de myVar
.
La palabra clave synchronized
hace más que un acceso exclusivo de un hilo a un bloque o un método. También garantiza que el segundo hilo esté informado sobre un cambio de una variable.
Por lo tanto, debe marcar ambos métodos como synchronized
o marcar el miembro myVar
como volatile
.
Aquí hay una muy buena explicación sobre la sincronización:
Las acciones atómicas no se pueden intercalar, por lo que se pueden usar sin temor a la interferencia de hilos. Sin embargo, esto no elimina toda necesidad de sincronizar acciones atómicas, porque los errores de consistencia de la memoria aún son posibles. El uso de variables volátiles reduce el riesgo de errores de consistencia de la memoria, ya que cualquier escritura en una variable volátil establece una relación de pasar antes a las lecturas posteriores de esa misma variable. Esto significa que los cambios en una variable volátil siempre son visibles para otros hilos. Lo que es más, también significa que cuando un hilo lee una variable volátil, no solo ve el último cambio en el volátil, sino también los efectos secundarios del código que provocó el cambio.
No es seguro para subprocesos. Las variables de tipo long
y double
en Java se tratan como dos variables separadas de 32 bits. Un hilo puede estar escribiendo y ha escrito la mitad del valor cuando otro hilo lee ambas mitades. En esta situación, el lector vería un valor que nunca se suponía que existiera.
Para que este hilo sea seguro, puede declarar myVar
como volatile
(Java 1.5 o posterior) o hacer que tanto setIt
como setIt
getIt
synchronized
.
Tenga en cuenta que incluso si myVar
fuera una int
32 bits, podría encontrarse con problemas de subprocesamiento en los que un subproceso podría estar leyendo un valor desactualizado que otro subproceso ha cambiado. Esto podría ocurrir porque el valor ha sido guardado en la memoria caché por la CPU. Para resolver esto, nuevamente debe declarar myVar
como volatile
(Java 1.5 o posterior) o hacer que tanto setIt
como setIt
getIt
synchronized
.
También vale la pena señalar que si está utilizando el resultado de getIt
en una llamada setIt
posterior, por ejemplo, x.setIt(x.getIt() * 2)
, entonces probablemente quiera synchronize
en ambas llamadas:
synchronized(x)
{
x.setIt(x.getIt() * 2);
}
Sin la sincronización adicional, otro subproceso podría cambiar el valor entre las llamadas getIt
y setIt
, lo que provocaría la pérdida del valor del otro subproceso.
No, no es. Al menos, no en plataformas que carecen de acceso a memoria atómica de 64 bits.
Supongamos que el subproceso A llama a setIt
, copia 32 bits en la memoria donde está el valor de la copia de seguridad, y luego se elimina antes de que pueda copiar los otros 32 bits.
Entonces el hilo B llama a getIt
.
No, no, porque los largos no son atómicos en Java, por lo que un hilo podría haber escrito 32 bits del largo en el método setIt, y luego el get podría leer el valor y luego establecerlo. Podría establecer los otros 32 bits.
Entonces, el resultado final es que getIt devuelve un valor que nunca fue válido.
El captador no es seguro para subprocesos porque no está protegido por ningún mecanismo que garantice la visibilidad más actualizada. Tus elecciones son:
- haciendo
myVar
final (pero luego no puedes mutarlo) - haciendo que
myVar
volátil - usar sincronizado para acceder
myVar
AFAIK, las JVM modernas ya no dividen operaciones largas y dobles. No sé de ninguna referencia que indique que esto todavía es un problema. Por ejemplo, vea AtomicLong que no usa sincronización en la JVM de Sun.
Suponiendo que quiere asegurarse de que no sea un problema, puede usar la sincronización de get () y set (). Sin embargo, si está realizando una operación como agregar, es decir, establecer (obtener () + 1), esta sincronización no le cuesta mucho, aún tiene que sincronizar el objeto para toda la operación. (Una mejor forma de evitar esto es usar una sola operación para add (n) que está sincronizado)
Sin embargo, una mejor solución es usar un AtomicLong. Esto es compatible con operaciones atómicas como get, set y add, y NO utiliza la sincronización.