2011-09-30 13 views
9

Sto eseguendo una revisione del codice per un cambiamento in un prodotto Java che non possiedo. Non sono un esperto di Java, ma sospetto fortemente che questo sia inutile e indica un fondamentale fraintendimento di come funziona la sincronizzazione.Uso confuso di sincronizzazione in Java: pattern o anti-pattern?

synchronized (this) { 
    this.notify(); 
} 

Ma potrei sbagliarmi, dal momento che Java non è il mio parco giochi principale. Forse c'è una ragione per cui questo è fatto. Se mi puoi illuminare su cosa stava pensando lo sviluppatore, lo apprezzerei.

+6

I * credo * questo è più appropriato per http://codereview.stackexchange.com – BalusC

+1

materiale di lettura: http://stackoverflow.com/questions/442564/avoid-synchronizedthis-in- java – claymore1977

+3

Ti stai chiedendo se la sincronizzazione è necessaria prima della chiamata a 'notify()'? Il thread ** deve contenere il monitor dell'oggetto quando chiama 'notify()', quindi è necessario in generale. –

risposta

10

certamente non è inutile, si può avere un altro thread che ha un riferimento all'oggetto che contiene il codice di cui sopra facendo

synchronized(foo) { 
    foo.wait(); 
} 

al fine di essere svegliati quando succede qualcosa. Sebbene, in molti casi, sia consigliabile eseguire la sincronizzazione su un oggetto di blocco interno/privato anziché su this.

Tuttavia, solo facendo un .notify() all'interno del blocco di sincronizzazione potrebbe essere del tutto sbagliato - di solito si hanno del lavoro da fare e notificare quando è fatto, che nei casi normali ha anche bisogno di essere fatto atomicamente per quanto riguarda altri thread. Dovremmo vedere più codice per determinare se sia davvero sbagliato.

+0

Sembra ovvio in retrospettiva. Ora ho bisogno di districare come funziona tutto questo. –

+0

Beh, non riesco a trovare dove chiama this.wait(), quindi ho inviato un feedback chiedendo se è davvero necessario. –

1

Questo in genere non è un anti-pattern, se si desidera utilizzare ancora i blocchi intrinseci. Alcuni potrebbero considerare questo come un modello anti, in quanto i nuovi blocchi espliciti da java.util.concurrent sono più a grana fine.

Ma il tuo codice è ancora valido. Ad esempio, tale codice può essere trovato in una coda di blocco, quando un'operazione di blocco è riuscita e un altro thread in attesa deve essere notificato. Si noti tuttavia che i problemi di concorrenza dipendono in larga misura dall'utilizzo e dal codice circostante, quindi il semplice snippet non è così significativo.

1

La documentazione dell'API Java per Object.notify() indica che il metodo "deve essere chiamato solo da un thread che è il proprietario del monitor di questo oggetto". Quindi l'uso potrebbe essere legittimo a seconda del contesto circostante.

2

Questo va perfettamente bene. In base allo Java 6 Object#notify() api documentation:

Questo metodo deve essere chiamato solo da un thread proprietario del monitor di questo oggetto.

+0

"Fine" come in un uso corretto dell'API. Ma l'OP chiedeva qualcosa che era un * modello * o * antipattern *. – Raedwald

3

Se questo è tutto ciò che è nel blocco sincronizzata allora è un antipattern, il punto di sincronizzazione è di fare qualcosa all'interno del blocco, impostando qualche condizione, quindi chiamare notify o notifyAll di svegliarsi una o più thread in attesa.

Quando si utilizza attesa e comunicare è necessario utilizzare una variabile di condizione, vedi this Oracle tutorial:

Nota: invocano sempre attesa all'interno di un ciclo che i test per la condizione di essere aspettato. Non dare per scontato che l'interruzione riguardasse la particolare condizione che si stava aspettando o che la condizione fosse ancora vera.

Non si dovrebbe supporre che hai ricevuto una notifica solo perché un filo esce da una chiamata a Object # aspettare, per diverse ragioni:

  • Quando si chiama la versione di attesa che assume un valore di timeout non c'è modo di sapere se l'attesa è terminata a causa della ricezione di una notifica o della scadenza.

  • È necessario tenere conto della possibilità che un thread si possa riattivare dall'attesa senza aver ricevuto una notifica (il "wakeful spurio").

  • Il thread in attesa che riceve una notifica deve ancora riacquisire il blocco che ha abbandonato quando ha iniziato ad attendere, non c'è nessun collegamento atomico di questi due eventi; nell'intervallo tra la notifica e la riacquisizione del blocco, un altro thread può agire e, eventualmente, modificare lo stato del sistema in modo che la notifica non sia valida.

  • È possibile avere un caso in cui il thread di notifica agisce prima che qualsiasi thread sia in attesa in modo che la notifica non abbia alcun effetto. Supponendo che un thread entri in attesa prima che l'altro thread lo notifichi è pericoloso, se hai torto il thread in attesa si bloccherà indefinitamente.

Quindi una notifica di per sé non è abbastanza buono, si finisce per indovinare sul fatto che una notifica è accaduto quando l'attesa/notifica API non ti dà abbastanza informazioni per sapere cosa sta succedendo. Anche se altri lavori che il thread di notifica sta facendo non richiede la sincronizzazione, l'aggiornamento della variabile condizione lo fa; dovrebbe esserci almeno un aggiornamento della variabile di condizione condivisa nel blocco sincronizzato.

+0

Questo è quello che pensavo, ma a volte è possibile che si desideri sincronizzare la sincronizzazione. –

+1

Questo non è sicuramente sempre vero. Forse la cosa da fare prima che la notifica non abbia bisogno di essere sincronizzata. Non c'è niente di male nella sincronizzazione granulare, che di solito è una buona cosa. Un semplice esempio potrebbe essere un thread in background che è in attesa all'utente di fare clic su un pulsante (tramite l'attesa su un oggetto). Quando Swing notifica ActionListener, potrebbe eseguire la notifica utilizzando il codice sopra riportato. –

+0

@Mark suona molto pericoloso se la condizione viene aggiornata senza protezione; l'altro thread che esamina la condizione può vedere lo stato incoerente. – irreputable