2009-06-13 12 views
11

pregunta posiblemente similar:¿Por qué las variables en Java no son volátiles por defecto?

Do you ever use the volatile keyword in Java?


Hoy estaba depurando mi juego; Tenía un problema de enhebrado muy difícil que aparecía cada pocos minutos, pero era difícil de reproducir. Así que primero agregué la palabra clave synchronized a cada uno de mis métodos. Eso no funcionó. Luego agregué la palabra clave volatile a cada campo. El problema parecía arreglarse solo.

Después de un poco de experimentación, encontré que el responsable del campo era un objeto GameState que hacía un seguimiento del estado actual de mi juego, que puede ser jugar u ocupado. Cuando está ocupado, el juego ignora la entrada del usuario. Lo que tenía era un hilo que cambiaba constantemente la variable state, mientras que el hilo del evento lee la variable state. Sin embargo, después de que un hilo cambia la variable, el otro hilo tarda varios segundos en reconocer los cambios, lo que finalmente causa el problema.

Se corrigió haciendo que la variable de estado volatile.

¿Por qué no hay variables en Java volatile de forma predeterminada y cuál es el motivo para no utilizar la palabra clave volatile?

+11

Por la misma razón, los métodos no están sincronizados por defecto. –

+6

@mP: No lo creo. Volátiles y sincronizados no son equivalentes: la volatilidad no puede causar interbloqueos. –

Respuesta

32

Para abreviar, las variables volátiles, ya sean en Java o C#, nunca se almacenan en la memoria caché localmente dentro de la secuencia. Esto no tiene mucha implicación a menos que se trate de una CPU multiprocesador/multinúcleo con subprocesos que se ejecutan en diferentes núcleos, ya que estarían buscando el mismo caché. Cuando declara una variable como volátil, todas las lecturas y escrituras vienen directamente de la ubicación de la memoria principal real; no hay memoria caché involucrada. Esto tiene implicaciones cuando se trata de optimización, y hacerlo innecesariamente (cuando la mayoría de las variables no necesitan ser volátiles) estaría infligiendo una penalización en el rendimiento (por insignificante que sea o no) para una ganancia relativamente pequeña.

+6

Incluso con un solo procesador central, el compilador (incluido el compilador JIT) puede decidir optimizar el acceso a la variable. –

+0

@Chris: procedo de un fondo .NET, por lo que Java puede ser diferente, pero no creo que una variable de instancia pueda optimizarse a menos que detecte que nunca se usó y las variables locales no se pueden declarar volátiles. (No lo necesitaría realmente), ¿verdad? –

+8

@ Adam: No se puede optimizar totalmente. Sin embargo, algo como "y = x * x" solo puede ir a la memoria para obtener el valor de x una vez, en lugar de dos. Es un ejemplo trivial ... ahora imagine que x se extiende a lo largo de un ciclo y se comprueba varias veces. Si el compilador nunca lo ve a usted cambiar el valor de x, podría decidir leerlo una vez y guardarlo localmente. –

5

Porque el compilador no puede optimizar las variables volátiles.

volatile le dice al compilador que la variable puede cambiar en cualquier momento. Por lo tanto, no puede suponer que la variable no cambiará y optimizará en consecuencia.

2

Declarar variables volátiles generalmente tiene un gran impacto en el rendimiento. En los sistemas tradicionales de un solo hilo, era relativamente fácil saber qué debía ser volátil; fueron esas cosas que accedieron al hardware.

En multiproceso, puede ser un poco más complejo, pero generalmente recomendaría el uso de notificaciones y colas de eventos para manejar los datos de paso entre los pads en un montón de variables mágicas. En Java puede no importar mucho; en C/C++ te meterías en problemas cuando el hardware subyacente no pueda establecer esas variables atómicamente.

+0

Tengo curiosidad, mi única experiencia con variables volátiles es específicamente para acceso multiproceso. ¿Cuál sería el razonamiento detrás de las variables volátiles en un entorno de subproceso único? –

+1

@ Adam: en Java y C#, no estoy tan seguro, aunque creo que JNI podría permitirte jugar algunos juegos. En un entorno C/C++, volátil fue fundamental para indicar que esta memoria puede cambiarse desde otro lugar además del programa en ejecución, es decir, el hardware que está mapeado a esa ubicación de memoria. Como Java y C# generalmente no se ejecutan tan cerca del hardware, puede ser un juego diferente. –

+0

@AdamRobinson: incluso en un entorno de subproceso único, Java se reserva el derecho de reordenar comandos independientes. Entonces, por ejemplo: 'x = 1; y = 2; 'puede cambiarse a' y = 2; x = 1; '. Pero 'x = z ++; y = z ++; 'no debería cambiarse a' y = z ++; x = z ++; '. Se supone que JVM no debe reordenar los comandos que tienen visibilidad externa. Por ejemplo 'x = 1; System.out.println ("x está establecido"); y = 2; 'no puede ser reordenado. Si no confía en la lógica de visibilidad externa de Javas (por ejemplo, si está monitoreando la memoria real), puede usar volátil para marcar la variable como algo que no debería optimizarse. –

6

Mientras que otros son correctos al señalar por qué sería una mala idea volátil por defecto, hay otro punto que hacer: es muy probable que haya un error en su código. Las variables rara vez necesitan ser volátiles: siempre hay una forma de sincronizar correctamente el acceso a las variables (ya sea por palabra clave sincronizada o usando objetos AtomicXxx desde java.util.concurrency): las excepciones incluirían el código JNI manipulando estos (que no está limitado por directivas de sincronización).

De modo que en lugar de agregar volátiles, es posible que desee averiguar POR QUÉ resolvió el problema. No es la única forma de resolverlo, y probablemente haya una mejor manera.

+1

Si bien esto puede o no ser cierto, es muy posible que intente escribir código sin candado. No siempre es necesario obtener un candado exclusivo o usar un semáforo si todo lo que estás haciendo es leer. Solo mi .02 –

+0

Es verdad, es posible, y los volátiles son de una sola manera. Pero la sobrecarga de volátil (debido a razones que otros señalan) puede exceder la de la sincronización explícita y es poco probable que sea menor. Especialmente teniendo en cuenta la optimización de las impls j.u.concurrent, o incluso una sincronización simple. – StaxMan

10

Los volátiles realmente solo son necesarios cuando intenta escribir bajo nivel de seguridad de subprocesos, código de bloqueo. La mayoría de su código probablemente no debería ser ni thread-safe o sin bloqueos. En mi experiencia, la programación sin bloqueos solo vale la pena intentarlo después de que hayas encontrado que la versión más simple que hace si el bloqueo está teniendo un impacto de rendimiento significativo debido al bloqueo.

La alternativa más agradable es usar otros bloques de construcción en java.util.concurrent, algunos de los cuales están libres de cerrojo, pero no se meta tanto con la cabeza como tratando de hacerlo todo usted mismo en un nivel bajo.

Volatility tiene sus propios costos de rendimiento, y no hay ninguna razón por la que el código más incurra en esos costos.

8

Personalmente creo que los campos deberían haber sido definitivos por defecto y mutables solo con una palabra clave adicional, pero ese barco ha navegado hace mucho tiempo. ;)

Cuestiones relacionadas