In JCIP, sezione 3.2.1 "Pratiche di costruzione sicura", è presente un avviso per la perdita di this
in un altro thread dal costruttore, "anche se la pubblicazione è l'ultima istruzione nel costruttore". Quest'ultima parte mi sembra troppo forte, ed è fornita senza giustificazione. Cosa succede dopo la costruzione che devo essere così attento a evitare? Ci sono delle eccezioni? Sono interessato perché recentemente ho presentato del codice in cui ho fatto proprio questo, e voglio decidere se c'è una giustificazione per tornare indietro e refactoring.java: perché non dovrebbe essere permesso di sfuggire al costruttore?
risposta
Per quanto riguarda il modello di memoria Java, l'uscita del costruttore svolge un ruolo nella semantica del campo finale, quindi c'è una differenza se un'istruzione è prima o dopo l'uscita del costruttore.
This works This doesn't work
-------------------------------------------------------------
static Foo shared; static Foo shared;
class Foo class Foo
{ {
final int i; final int i;
Foo() Foo()
{ {
i = 1; i = 1;
shared = this;
} }
} }
shared = new Foo(); new Foo();
(Nota: shared
non è volatile, la pubblicazione è attraverso gara di dati.)
L'unica differenza tra i 2 esempi sta assegnando shared
prima o dopo l'uscita del costruttore. Nel secondo esempio, i=1
può essere riordinato dopo l'assegnazione.
Tuttavia, se la pubblicazione è un'azione sincronizzata, ad es. attraverso una variabile volatile, quindi va bene; altri thread osserveranno un oggetto completamente inizializzato; i campi non hanno nemmeno bisogno di final
.
La pubblicazione tramite dati race (o qualsiasi altra cosa tramite la data race) è un'attività molto complessa che richiede un ragionamento molto accurato. Se si evita la corsa ai dati, le cose sono molto più semplici. Se il codice non contiene dati, non c'è differenza tra la perdita di this
immediatamente prima dell'uscita del costruttore e la pubblicazione immediata dopo l'uscita del costruttore.
Non si dovrebbe mai perdere this
da un costruttore in qualsiasi punto, "anche [...] nell'ultima istruzione." Dal momento che lo this
non è completamente costruito, possono accadere cose molto strane. Vedi this SO answer su una domanda molto simile.
Non si dovrebbe mai passare this
fuori del costruttore (noto come "perdite this
")
Una ragione per cui non si dovrebbe fare, anche se è l'ultima riga del costruttore, è che la JVM è consentito per riordinare le istruzioni finché l'effetto sul thread corrente non è influenzato. Se this
viene passato a un processo in esecuzione in un altro thread, il riordino può causare bug strani e sottili.
Un'altra ragione è che le sottoclassi possono fornire la propria inizializzazione, quindi la costruzione potrebbe non essere completa nell'ultima riga del costruttore della classe.
- 1. Metodi Sonar non dovrebbe essere vuota al costruttore
- 2. Perché questo non dovrebbe essere compilato?
- 3. Perché la classe Singleton dovrebbe essere sigillata?
- 4. Perché il costruttore ereditato non dovrebbe ereditare gli argomenti predefiniti?
- 5. Perché non c'è nessuna chiamata al costruttore?
- 6. MongoDB vs TokuMX - Perché non dovrebbe essere usato TokuMX per impostazione predefinita al posto di MongoDB
- 7. "chiamata al costruttore deve essere la prima istruzione in un costruttore" questione in Java
- 8. Perché un TypedArray dovrebbe essere riciclato?
- 9. Perché dovrebbe @@ class_variables essere evitato in Ruby?
- 10. Perché la chiamata # dell'applicazione dovrebbe essere lenta?
- 11. Perché una funzione virtuale dovrebbe essere privata?
- 12. Perché Flask non dovrebbe essere distribuito con il server integrato?
- 13. Perché mysql_real_escape_string() non dovrebbe evitare SQL Injection?
- 14. Perché dovrebbe essere (ogni? Stringa? []) Vero?
- 15. Cosa dovrebbe essere OO e cosa non dovrebbe?
- 16. Perché il compilatore Java non dovrebbe supportare l'ereditarietà delle importazioni?
- 17. Dove dovrebbe essere Android.mk?
- 18. La cartella .nuget dovrebbe essere aggiunta al controllo di versione?
- 19. Perché il modello di osservatore dovrebbe essere deprecato?
- 20. Quali caratteri sono sicuri di non sfuggire al contesto di valori CSS?
- 21. Java backspace sfuggire
- 22. F #: L'operatore '&' non dovrebbe normalmente essere ridefinito
- 23. ModelState.IsValid anche quando non dovrebbe essere?
- 24. dovrebbe mai essere usato encodeURI?
- 25. Quando dovrebbe essere una classe .NET Override()? Quando non dovrebbe?
- 26. Convenzione in java - "nuovo" al di fuori del costruttore/metodo?
- 27. Perché Delphi consente ai parametri del costruttore di non essere corretti?
- 28. Perché qualcuno dovrebbe utilizzare Collections.emptyList in java?
- 29. Un tipo dovrebbe essere solo in movimento, solo perché la copia può essere costosa?
- 30. Cosa dovrebbe essere hadoop.tmp.dir?
http://msmvps.com/blogs/jon_skeet/archive/2010/09/02/don-t-let-this-get-away.aspx – SLaks