2010-06-03 25 views
14

Supongamos que tengo dos hilos y un objeto. Un hilo asigna el objeto:¿Cómo puedo usar correctamente la palabra clave volátil en Java?

public void assign(MyObject o) { 
    myObject = o; 
} 

Otro hilo utiliza el objeto:

public void use() { 
    myObject.use(); 
} 

¿La variable myObject deben ser declarados tan volátil? Estoy tratando de entender cuándo usar volátiles y cuándo no, y esto me desconcierta. ¿Es posible que el segundo hilo mantenga una referencia a un objeto viejo en su memoria caché local? ¿Si no, porque no?

Muchas gracias.

+1

¿Qué versión de Java estás usando? – Alerty

+0

Estoy desarrollando en un dispositivo móvil. Es básicamente java 1.4.x – Tiyoal

Respuesta

6

Dejando a los complicados detalles técnicos detrás, se puede ver volatile más o menos como un modificador de synchronizedvariables de. Cuando desea sincronizar el acceso a los métodos o bloques, entonces por lo general le gustaría utilizar el modificador synchronized de la siguiente manera:

public synchronized void doSomething() {} 

Si desea "sincronizar" el acceso a las variables, entonces' d gustaría usar el volatile modificador:

private volatile SomeObject variable; 

Detrás de las escenas que hacen cosas diferentes, pero la efecto es el mismo: los cambios son inmediatamente visibles para el próximo hilo que accede.

En su caso específico, no creo que el modificador volatile tenga algún valor. El volatile no garantiza de ninguna manera que el hilo que asigna el objeto se ejecutará antes de el hilo que utiliza el objeto. Puede ser tan bueno al revés. Probablemente solo quiera hacer una comprobación nula en el método use() primero.

actualización: ver también this article:

El acceso a las variables actúa como si que está encerrado en un bloque sincronizado, sincronizado sobre sí mismo. Decimos "actúa como si" en el segundo punto, porque para el programador al menos (y probablemente en la mayoría de las implementaciones de JVM) no existe un objeto de bloqueo real implicado.

+7

Esto es completamente incorrecto. Volátil se trata completamente de los efectos de memoria y la visibilidad de una variable, no acceso concurrente. – Kevin

+0

Estoy confundido. He estado leyendo y he leído que normalmente se usa volátil para compartir un marcador entre hilos: un hilo establece el hilo en falso y el otro dato que se ha establecido en falso. Si boolean no se declara como volátil, el segundo subproceso siempre notará que se ha establecido en falso, porque el valor podría haberse almacenado en la memoria caché local. ¿No es cierto o no entendí bien esa afirmación? – Tiyoal

+3

@Kevin: Sí, ese es el detalle técnico. Estaba hablando del ** efecto ** para que tenga más sentido para los principiantes. El cambio en la variable es inmediatamente visible para el siguiente subproceso de acceso. – BalusC

11

Estoy tratando de comprender cuándo utilizar volátil y cuando no

Debe sobre todo evitar su uso. Use una clase AtomicReference en su lugar (u otra atomic cuando corresponda). Los efectos de memoria son los mismos y la intención es mucho más clara.

Recomiendo leer el excelente Java Concurrency in Practice para una mejor comprensión.

+2

Gracias por el consejo. Estoy de acuerdo en que no tengo que usar estas 'cosas de bajo nivel'. Sin embargo, siento la necesidad de entender cómo funciona bajo el capó. El libro es una muy buena sugerencia para eso. Gracias. – Tiyoal

+0

@Tiyoal Para obtener información absoluta, consulte la sección 17.4 del JLS que trata sobre el Modelo de memoria: http://java.sun.com/docs/books/jls/third_edition/html/memory.html#17.4 – Kevin

+0

Segundo, la concurrencia en la práctica. Volatile es una forma más débil de sincronización ... está bien usarla si una variable está siendo escrita por 1 hilo y leída por muchos hilos para garantizar que las escrituras se vean de inmediato. – bwawok

2

Puede usar volátil en este caso.Necesitará una sincronización volátil alrededor del acceso a la variable o algún mecanismo similar (como AtomicReference) para garantizar que los cambios realizados en el hilo de asignación sean realmente visibles para el hilo de lectura.

+0

Pero, ¿cómo se cuida esto de que el hilo que asigna el objeto se ejecutará * antes * del hilo que usa el objeto? – BalusC

+0

¿Por qué necesito la sincronización aquí? ¿Declarar como volátil no es suficiente? – Tiyoal

+0

@Tiyoal - eso es un cualquiera o el otro. Necesitas usar una de esas opciones. – Robin

0

Hay algunos comentarios confusos aquí: Para aclarar, el código es incorrecto tal como está, suponiendo que dos hilos diferentes llaman assign() y use().

En ausencia de volatile, u otro sucede-antes de relación (por ejemplo, la sincronización en un bloqueo común) cualquier escribir a myObject en assign() no está garantizado para ser visto por el subproceso de llamada use() - no inmediatamente, no en de manera oportuna, y de hecho nunca.

Sí, volatile es una forma de corregir esto (suponiendo que esto sea un comportamiento incorrecto - ¡hay situaciones plausibles en las que no le importa esto!).

Tiene razón en que el subproceso 'usar' puede ver cualquier valor 'caché' de myObject, incluido el que se le asignó en el momento de la construcción y cualquier valor intermedio (nuevamente en ausencia de otros puntos anteriores).

4

Declarar una variable volátil de Java significa:

  • El valor de esta variable no se almacenan en caché hilo localmente
  • Acceso a variable actúa como si estuviera encerrado en un bloque sincronizado

El uso típico y más común de volátil es:

public class StoppableThread extends Thread { 
    private volatile boolean stop = false; 

    public void run() { 
    while (!stop) { 
     // do work 
    } 
    } 

    public void stopWork() { 
    stop = true; 
    } 
} 
1

He pasado bastante tiempo intentando comprender la palabra clave volatile. Creo que @aleroot ha dado el mejor y más simple ejemplo en el mundo.

Esto es a su vez mi explicación para los simulado (como yo :-)):

Escenario 1: Suponiendo que el stop no se ha declarado tan volátil continuación un hilo dado hace y 'piensa' lo siguiente:

  1. stopWork() se llama: tengo que establecer el stop a true
  2. Grande, lo hice en mi pila local, ahora tengo que actualizar el montón principal de la JVM.
  3. Vaya, JVM me dice que den un paso en la CPU a otro hilo, tengo que parar por un tiempo ...
  4. OK, estoy de vuelta. Ahora puedo actualizar el montón principal con mi valor. Actualizando ...

Scenario2: Ahora vamos a la stop declararse tan volátil:

  1. stopWork() se llama: Tengo que establecer el stop a true
  2. Grande, lo hice en mi pila locales ahora Tengo que actualizar el montón principal de JVM.
  3. Lo siento muchachos, tengo que hacer (2) AHORA - Me dicen que es volatile. Tengo que ocupar la CPU un poco más ...
  4. Actualizando el montón principal ...
  5. Bien, he terminado. Ahora puedo ceder.

Sin sincronización, sólo una idea simple ...

Por qué no declarar todas las variables volatile por si acaso? Debido a Scenario2/Step3. Es un poco ineficiente pero aún mejor que la sincronización regular.

Cuestiones relacionadas