2012-02-29 11 views
10

Espero que esto sea suficiente información, así que aquí va. Si necesita más información, déjelo saber en los comentarios.Cuándo usar sincronizado en Java

Tengo una clase que tiene dos clases internas. Las clases internas tienen dos métodos que llaman a un método en la clase externa. Por lo tanto, se ve así:

public OuterClass { 
    private boolean outerMethodHasBeenCalled = false; 

    private void outerMethod() { 
     if(!outerMethodHasBeenCalled) { 
      // do stuff 
     } 

     outerMethodHasBeenCalled = true; 
    } 

    private FirstInnerClass { 
     public void someMethod() { 
      outerMethod(); 
     } 
    } 

    private SecondInnerClass { 
     public void someOtherMethod() { 
      outerMethod(); 
     } 
    } 
} 

Es importante tener en cuenta que:

  • Se trata de una aplicación para Android. Las instancias de FirstInnerClass y SecondInnerClass se pasan a un WebView como una interfaz de JavaScript, por lo que se pueden llamar a someMethod y someOtherMethod en cualquier momento, sin ningún orden en particular.
  • Actualmente tengo un problema con el código existente (sin la palabra clave sincronizada) donde outerMethod se llama más o menos al mismo tiempo (imprimo un mensaje de registro, y están marcados con el tiempo de milésimas de segundo) diferentes objetos Mi aplicación luego 'hace cosas' dos veces porque outerMethodHasBeenCalled sigue siendo falso cuando se llamó a outerMethod. Esto no está bien, y es exactamente lo que intento evitar. Mi aplicación solo debe 'hacer cosas' una sola vez: la primera vez que se llama al outerMethod.
  • Puede parecer que tengo varias instancias de OuterClass, pero tenga la seguridad de que es solo una instancia de OuterClass.

Es importante que mi aplicación 'haga cosas' solo la primera vez que se llama outerMethod (espero que ya sea evidente). Todas las llamadas siguientes son ignoradas. Cualquiera que sea la clase interna que llame primero a outerMethod, no importa.

Entonces, ¿es apropiado usar la palabra clave sincronizada en este caso?

Respuesta

19

Sí, teniendo en cuenta lo ya ha dispuesto anteriormente, me gustaría ir con:

private synchronized void outerMethod() { 
... 
} 

Nota, esto tendrá el efecto secundario de bloqueo de una de las personas que llaman hasta el outerMethod() completa. Si eso es aceptable, genial. Si la intención es simplemente que el código en outerMethod() se ejecuta una vez, y que está bien que la segunda persona que llama no ser demorado si la primera persona que llama está ejecutando outerMethod(), usted podría considerar:

public OuterClass { 
    private AtomicBoolean outerMethodHasBeenCalled = new AtomicBoolean(); 

    private void outerMethod() { 
     if (outerMethodHasBeenCalled.compareAndSet(false, true)) { 
      // do stuff 
     } 
    } 
... 

Consulte el JavaDoc for AtomicBoolean para conocer qué está sucediendo allí (suponiendo que esté disponible en Java de Android).

+9

+1 para AtomicBoolean, aprendí algo nuevo :) – quaylar

7

envolver todo en outerMethod que desea ejecutar sólo una vez en un bloque sincronizado:

private void outerMethod() { 
    synchronized (this) { 
     if(!outerMethodHasBeenCalled) { 
      // do stuff 
     } 

     outerMethodHasBeenCalled = true; 
    } 
} 

De esta manera, la primera vez que se llama al método, sólo un hilo se permitirá en el bloque sincronizado a una hora. El primero ejecutará el código en la instrucción if, luego establecerá outerMethodHasBeenCalled en true. Los otros hilos verán que es verdadero y omitirán el código if.

+0

¿no es necesario también hacer que la bandera sea 'volátil' (para que otros subprocesos puedan ver los cambios de manera confiable)? Tal vez use un AtomicBoolean para estar seguro. – Thilo

+1

@Thilo: si todos los accesos a la bandera están dentro de los bloques sincronizados, no es necesario usar 'volátil'.El modelo de memoria de Java asegura que los cambios serán visibles. AtomicBoolean y volátil son útiles cuando no desea tener el costo de un bloque sincronizado completo, aunque son más difíciles de usar. – Avi

+2

@Thilo no, no es necesario, si el único lugar al que se accede a outerMethodHasBeenCalled es en outerMethod. La palabra clave sincronizada denota una barrera de sincronización de memoria. Lea la especificación de VM en la palabra clave sincronizada. Por cierto, los pls no hacen cosas solo para estar seguros. Comprenda exactamente lo que está escribiendo y las implicaciones. Otros programadores que entran en tu código después de ti, que SÍ entienden, serán temporalmente rastreados tratando de descubrir por qué se usó una construcción, p. "Debe usar volátiles por alguna razón, ¿qué es lo que no estoy viendo?" – brettw

Cuestiones relacionadas