2009-11-22 13 views
14

En Java, la asignación es atómica si el tamaño de la variable es menor o igual a 32 bits, pero no si es más de 32 bits.¿Volátil o sincronizado para el tipo primitivo?

¿Qué (volátil/sincronizado) sería más eficiente de usar en caso de asignación doble o larga?

igual,

volatile double x = y; 

sincronizada no es aplicable con el argumento primitivo. ¿Cómo uso sincronizado en este caso? Por supuesto, no quiero bloquear mi clase, por lo que this no se debe utilizar.

+0

¿Por qué "no desea bloquear [su] clase"? (Tenga en cuenta que el bloqueo de un método de instancia solo se lleva a cabo en el objeto mismo). Tenga en cuenta también que las JVM modernas son extremadamente avanzadas y la sobrecarga de rendimiento de la sincronización generalmente no es un problema –

+0

Tengo un jsp donde muchas personas acceden/actualizan los datos. los datos están en un objeto de caché y allí necesito estas sincronizaciones y et al. porque la cantidad de personas podría ser numerosa. – DKSRathore

+1

Creo que debería ir con AomicDouble. – DKSRathore

Respuesta

5

Si encuentra que el bloqueo en el objeto mismo es demasiado pesado, entonces sincronizado es el camino a seguir. Antes de Java 1.5 Volatile puede haber sido una buena opción, pero ahora volátil puede tener un gran impacto forzando la ordenación de instrucciones en el método donde ocurre la asignación. Cree un objeto por separado (private final Object X_LOCK = new Object();) y sincronícelo cuando configure u obtenga el valor de ese doble. Esto le dará un buen nivel de control sobre el bloqueo, que parece que necesita.

En el nuevo paquete de simultaneidad hay más opciones, como AtomicReference, que puede ser un buen sustituto de la volatilidad si realmente necesita evitar la sincronización.

+1

¿Estás diciendo que 'volátil 'es menos eficiente? – Pacerier

+0

@Pacerier, realmente depende (@oxbow_lakes alcanza el punto de rendimiento bastante bien), pero ahora tiene un mayor impacto (con 1.5 y más) de lo que solía hacerlo, y es posible ser más minimalista. En la práctica, este debería ser un código bastante usado con mucha contención y buenas oportunidades para que el orden de las instrucciones importe. Por lo tanto, no es una optimización de rendimiento válida sin evidencia que sea un problema en su caso específico (realmente hay muy pocas cosas que sí lo son). – Yishai

+1

La sincronización en un objeto tiene el mismo sucede, antes de ordenar semántica. – eckes

26

¿Qué estás tratando de hacer? Las palabras clave synchronized y volatile son mecanismos en Java que se pueden usar para garantizar que los diferentes hilos que observan los mismos valores observen valores consistentes. En particular, le permiten razonar acerca de sucede antes de las relaciones en sus programas.

Simplemente no puede evitar el uso de uno de volatile o synchronized para acceder correctamente a los campos no final en un programa multiproceso. Dicho esto, la razón principal por la que es probable que requiera synchronized sobre volatile es el requisito para usar operaciones de comparación atómica y establecer (es decir, no será ninguna consideración de rendimiento). Por ejemplo, en un multi-hilo programa:

volatile int i = 0; 

public void foo() { 
    if (i == 0) i = i + 1; 
} 

El código anterior es inherentemente insegura, a pesar de la declaración de la variable como medios volátiles que lee y escribe se vuelcan a la memoria principal - la única aplicación segura de tal El método sería algo así como:

int i = 0; 

public synchronized void foo() { 
    if (i == 0) i = i + 1; 
} 

¿Cuál debería preferir? Bueno, si tiene varios hilos que modifican un campo que depende del valor de ese campo (es decir, comparar y establecer), entonces synchronized es la única solución segura.

También vale la pena decir: la sobrecarga de rendimiento de synchronized no es un problema (en la gran mayoría de los casos). Los problemas de sincronización de rendimiento generalmente se deben a cuellos de botella innecesarios, bloqueos o bloqueos y se pueden mitigar si es necesario. Cualquier puros reloj ciclos sobrecarga será eclipsada por otras cosas que la aplicación hace: archivo IO, consultas de bases de datos, etc. interacción remota

+3

Volátil no es seguro ni siquiera para 'i ++' si 'i' es' doble' o 'largo '. – abyx

+0

Sí, pero cuál preferiría en tales casos. – DKSRathore

+0

Mi punto era acerca de comparar y establecer; 'if (i == 0) i ++' no es seguro, incluso cuando soy 'int' –

2

KDSRathore, puede utilizar algunos bloqueos explícitos, o hacer algún objeto objeto ficticio = new Object (), en el cual se sincroniza en setter/getter de ese doble

+1

Se recomienda más utilizar las envolturas atómicas existentes para las primitivas que implementarlas usted mismo. – abyx

+0

¿Dónde dice que las envolturas atómicas existentes son preferibles a un bloqueo dedicado? Un bloqueo dedicado puede ser mejor ya que el alcance del bloqueo es limitado. –

3

volátil es sin duda el camino a seguir si solo está haciendo una tarea. Estoy seguro de que lo sabes, pero desde que surgió: si quieres hacer operaciones más complejas (incrementar el valor, por ejemplo) necesitarías sincronizar. i ++ nunca es seguro para ningún tipo de variable. Necesitas sincronizar. i ++ y similares, ya que en realidad es más de 1 operación.

No

: Se expresó que usted podría utilizar AtomicDouble pero actualmente no existe una AtomicDouble en java.util.concurrent.atomic

Si usted está haciendo un múltiples operaciones en X, que requiere ajustarlo a un nuevo valor al final, es posible hacerlo de una manera segura para hilos sin ningún tipo de bloqueo, y que sea seguro para hilos, usando compare y set. Ejemplo:

AtomicLong x = new AtomicLong(SomeValue); 
public void doStuff() { 
    double oldX; 
    double newX; 
    do { 
    oldX = x.get(); 
    newX = calculateNewX(oldX); 
    } while (!x.compareAndSet 
     (Double.doubleToLongBits(oldX), Double.doubleToLongBits(newX))); 

Esto funciona porque compareAndSet verá si el valor de x ha cambiado desde la última vez que lo lea. Si x ha cambiado, entonces está obligado a hacer el cálculo nuevamente y volver a intentar configurarlo.

Por supuesto, podría implementar su propio AtomicDouble en lugar de hacer estas conversiones de doubleToLongBits. Eche un vistazo a AtomicFieldUpdater.

+0

Gracias por una buena respuesta. Sí, miré, no hay AtomicDouble, así que terminé bloqueando un objeto ficticio y haciendo mi doble asignación. Según lo volátil está bien. Pero, ¿es esto cierto en el caso del doble? – DKSRathore

+1

Bueno, el hecho de que AtomicLong define su valor como volátil y la definición de AtomicLong.set es simplemente "value = newValue", tomaría eso como una buena evidencia de que las variables volátiles de 64 bits tienen asignaciones atómicas a partir de java 1.6. –

+0

Es un ejemplo bien escrito con una intención inusual: gira hasta que finalmente es posible sobrescribir el valor 'x'. Más típico sería calcular un valor actualizado, intentar almacenarlo y, en caso de falla, renunciar, asumiendo que otro subproceso entró allí y avanzó la situación primero. El contrato en su ejemplo es que cada hilo competidor avanzará el valor una vez, haciendo lo que sea necesario frente a la contención. – seh

0

Según the oracle documentation, puede utilizar volátil para referirse al doble objetivo:

volatile Double x = y; 

"escribe y lee las referencias son siempre atómica, independientemente de si se implementan como de 32 bits o de 64 valores de bit ".

Cuestiones relacionadas