2012-02-29 17 views
10

Spero che questa informazione sia sufficiente, quindi ecco qui. Se hai bisogno di maggiori informazioni, fammelo sapere nei commenti.Quando utilizzare sincronizzato in Java

Ho una classe che ha due classi interne. Le classi interne hanno due metodi che chiamano un metodo nella classe esterna. Così, sembra che questo:

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(); 
     } 
    } 
} 

E 'importante notare che:

  • Questo è per un'applicazione Android. Le istanze di FirstInnerClass e SecondInnerClass vengono passate a una WebView come interfaccia JavaScript, pertanto someMethod e someOtherMethod possono essere richiamate in qualsiasi momento, senza un ordine particolare.
  • Attualmente ho un problema con il codice esistente (senza la parola chiave sincronizzata) dove outerMethod viene chiamato più o meno alla stessa ora (stampo un messaggio di registro e sono timestampati al millesimo di secondo) oggetti diversi. La mia app quindi "fa le cose" due volte perché outerMethodHasBeenCalled è ancora falso quando è stato chiamato outerMethod. Questo non è ok, ed è esattamente quello che sto cercando di prevenire. La mia app dovrebbe "fare cose" una sola volta e una sola volta: la prima volta viene chiamato outerMethod.
  • Potrebbe sembrare che io abbia più istanze di OuterClass, ma ho la certezza che è solo un'istanza di OuterClass.

È importante che la mia app "faccia cose" solo la prima volta che venga chiamato outerMethod (spero che sia evidente ormai). Tutte le chiamate successive vengono essenzialmente ignorate. Qualunque classe interna chiami prima outerMethod - non importa.

Quindi, è appropriato utilizzare la parola chiave sincronizzata in questo caso?

risposta

19

Yup, dato quello che hai steso sopra, mi piacerebbe andare con:

private synchronized void outerMethod() { 
... 
} 

Nota, questo avrà l'effetto collaterale di bloccare uno dei chiamanti fino alla outerMethod() completa. Se è accettabile, fantastico. Se l'intento è semplicemente che il codice in outerMethod() viene eseguito una volta, e è OK per il secondo chiamante non deve essere ritardata se il primo interlocutore è in funzione outerMethod(), si potrebbe considerare:

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

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

Vedere lo JavaDoc for AtomicBoolean per eliminare ciò che sta accadendo lì (supponendo che sia disponibile in Java di Android).

+9

+1 per AtomicBoolean, imparato qualcosa di nuovo :) – quaylar

7

Wrap tutto in outerMethod che si desidera eseguire solo una volta in un blocco sincronizzato:

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

     outerMethodHasBeenCalled = true; 
    } 
} 

In questo modo, la prima volta che il metodo viene chiamato, un solo thread sarà consentito nel blocco sincronizzato ad un tempo. Il primo eseguirà il codice nell'istruzione if, quindi imposta outerMethodHasBeenCalled in true. Gli altri thread vedranno che è vero e salteranno il codice if.

+0

non è necessario rendere il flag 'volatile' (in modo che altri thread possano vedere le modifiche in modo affidabile)? Forse usare un AtomicBoolean per essere al sicuro. – Thilo

+1

@Thilo: se tutti gli accessi alla bandiera sono all'interno di blocchi sincronizzati, non è necessario utilizzare 'volatile'.Il modello di memoria Java garantisce che le modifiche siano visibili. AtomicBoolean e volatile sono utili quando non si desidera avere il costo di un blocco sincronizzato completo, sebbene siano più complicati da utilizzare. – Avi

+2

@Thilo no non è necessario, se l'accesso alla sola outerMethodHasBeenCalled è eseguito in outerMethod. La parola chiave sincronizzata denota una barriera di sincronizzazione della memoria. Leggi le specifiche della VM sulla parola chiave sincronizzata. A proposito, i pls non fanno cose solo 'per essere al sicuro'. Comprendi esattamente ciò che stai scrivendo e le implicazioni. Altri programmatori che entrano nel tuo codice dopo che tu hai capito saranno temporaneamente rintracciati cercando di capire perché è stato usato un costrutto, ad es. "Deve usare volatile per una ragione, cosa non vedo?" – brettw

Problemi correlati