2011-11-22 8 views
5

Tengo una clase simple que hace algunos cálculos en su propio hilo e informa los resultados al oyente.java - ¿Tengo que declarar mi variable de miembro de escucha compartida como volátil?

class Calculator extends Thread { 
    protected Listener listener; 

    public void setListener(Listener l) { 
     listener = l; 
    } 

    public void run() { 
     while (running) { 
      ... do something ... 

      Listener l = listener; 

      if (l != null) { 
       l.onEvent(...); 
      } 
     } 
    } 
} 

En cualquier momento, el usuario puede llamar setListener (nulo) si él no quiere ningún evento para un determinado período de tiempo. Así, en el run() función, se crea una copia del oyente, por lo que no se puede ejecutar en una NullPointerExceptionlo que podría suceder si el oyente se establece en NULL después de la ! = Null comprobación del estado tuvo éxito . En mi caso, creo que esta es una alternativa correcta para sincronizarlo.

Mi pregunta es: ¿debo declarar aquí que la variable del miembro oyente es volátil? He estado leyendo mucho sobre volátil, pero todos los ejemplos parecen enfocarse en los tipos de datos básicos (booleano, int, ...) y no en Objetos. Por lo tanto, no estoy seguro de si los Objetos deberían/​​podrían declararse también volátiles. Creo que tengo que declararlo como volátil, por lo que el hilo siempre tiene la última versión de la variable miembro, pero no estoy seguro.

Gracias!

+0

Solo como sugerencia, no estoy respondiendo realmente a su pregunta, lo admito, pero ¿sería mejor poner un indicador de tipo "habilitado" en su Oyente, y luego, en lugar de tener al cliente configurado el Oyente nulo o no nulo? , ¿tiene setEnabled (true) o setEnabled (false)? Eso lo ayuda a resolver el problema de manejar un NPE inadvertido, y también evita que tenga que crear instancias periódicas de nuevos objetos Listener. –

+0

@Japer D., Esa es una solución realmente fea que tienes allí. – mre

+0

@JimKiley Estoy de acuerdo contigo. Sin embargo, estaba simplificando mi pregunta, por lo tanto, se ve un poco extraño. Disculpas por eso. –

Respuesta

5

Sí. Para garantizar que el subproceso Calculator ve un nuevo valor, establecido por otro subproceso, debería hacer que la variable sea volátil.

Sin embargo, volatile es un mecanismo de bajo nivel y rara vez se utiliza en el código de cliente. Le sugiero que considere usar java.util.concurrent.AtomicReference en este escenario, lo que asegura que todo funcione como se espera.

+0

Muy bien, gracias por la confirmación. Buscaré en AtomicReference, tengo que admitir que ignoré el nuevo paquete simultáneo durante demasiado tiempo. –

+0

Hehe ... yo también, pero eso es porque trato de evitar la concurrencia el mayor tiempo posible :-) – aioobe

+0

buen pensamiento :) –

3

Si utiliza este enfoque, ya no puede garantizar que un oyente no reciba una notificación de evento después de que setListener(null) regresa. Ejecución podría proceder de la siguiente manera:

Listener l = listener; // listener != null at this point 

// setListener(null) executes here 

if (l != null) { 
    l.onEvent(...); 
} 

Si tiene que ser garantizado que no hay eventos serán publicados en un oyente después de que haya sido registrado, entonces es necesario utilizar bloques sincronizados. Declarar listener ser volatile no ayudará. El código en su lugar debe ser:

public synchronized void setListener(Listener l) { 
    listener = l; 
} 

public void run() { 
    while (running) { 
     ... do something ... 

     synchronized (this) { 
      if (listener != null) { 
       listener.onEvent(...); 
      } 
     } 
    } 
} 

Si desea evitar el gasto de synchronized todo el tiempo, usted puede hacer esto:

if (listener != null) { 
    synchronized (this) { 
     if (listener != null) { 
      listener.onEvent(...); 
     } 
    } 
} 

Este corre el riesgo leve que se pierda un evento después configurando un oyente no nulo. Declarar que listener es volatile probablemente lo arreglaría.

+0

Esa es exactamente la razón por la que copio la variable miembro. Si se invoca setListener (null), la variable local l en run() no cambia. De esta manera, evito el bloque de sincronización. ¿No? –

+0

@TedHopp, no, ya que copia el contenido en una variable local. – aioobe

+0

@JaperD. - Me di cuenta y completamente revisé mi respuesta. –

Cuestiones relacionadas