Perché non avviare una discussione nel costruttore?

1. Panoramica

In questo rapido tutorial, vedremo perché non dovremmo avviare un thread all'interno di un costruttore.

Innanzitutto, introdurremo brevemente il concetto di pubblicazione in Java e JVM. Quindi, vedremo come questo concetto influisce sul modo in cui iniziamo i thread.

2. Pubblicazione ed Escape

Ogni volta che rendiamo disponibile un oggetto a qualsiasi altro codice al di fuori del suo ambito corrente, in pratica pubblichiamo quell'oggetto . Ad esempio, la pubblicazione avviene quando restituiamo un oggetto, lo memorizziamo in un riferimento pubblico o addirittura lo passiamo a un altro metodo.

Quando pubblichiamo un oggetto che non dovremmo avere, diciamo che l'oggetto è sfuggito .

Ci sono molti modi in cui possiamo lasciare sfuggire un riferimento a un oggetto, come pubblicare l'oggetto prima della sua costruzione completa. È un dato di fatto, questa è una delle forme più comuni di fuga: quando questo riferimento fuoriesce durante la costruzione dell'oggetto.

Quando questo riferimento sfugge durante la costruzione, altri thread potrebbero vedere quell'oggetto in uno stato non corretto e non completamente costruito. Questo, a sua volta, può causare strane complicazioni di sicurezza dei thread.

3. Fuga con fili

Uno dei modi più comuni per lasciare sfuggire questo riferimento è avviare un thread in un costruttore. Per capire meglio questo, consideriamo un esempio:

public class LoggerRunnable implements Runnable { public LoggerRunnable() { Thread thread = new Thread(this); // this escapes thread.start(); } @Override public void run() { System.out.println("Started..."); } }

Qui, passiamo esplicitamente il riferimento this al costruttore Thread . Pertanto, il thread appena avviato potrebbe essere in grado di vedere l'oggetto che lo racchiude prima che la sua costruzione completa sia completa. In contesti simultanei, ciò potrebbe causare piccoli bug.

È anche possibile passare implicitamente il riferimento this :

public class ImplicitEscape { public ImplicitEscape() { Thread t = new Thread() { @Override public void run() { System.out.println("Started..."); } }; t.start(); } }

Come mostrato sopra, stiamo creando una classe interna anonima derivata da Thread . Poiché le classi interne mantengono un riferimento alla loro classe che lo racchiude, il riferimento this sfugge nuovamente dal costruttore.

Non c'è niente di intrinsecamente sbagliato nella creazione di un thread all'interno di un costruttore. Tuttavia, è altamente sconsigliato per iniziare immediatamente , come la maggior parte del tempo, si finisce con una sfuggito questo riferimento, esplicitamente o implicitamente.

3.1. Alternative

Invece di avviare un thread all'interno di un costruttore, possiamo dichiarare un metodo dedicato per questo scenario:

public class SafePublication implements Runnable { private final Thread thread; public SafePublication() { thread = new Thread(this); } @Override public void run() { System.out.println("Started..."); } public void start() { thread.start(); } };:

Come mostrato sopra, pubblichiamo ancora il riferimento this al Thread. Tuttavia, questa volta, iniziamo il thread dopo il ritorno del costruttore:

SafePublication publication = new SafePublication(); publication.start();

Pertanto, il riferimento all'oggetto non sfugge a un altro thread prima della sua costruzione completa.

4. Conclusione

In questo breve tutorial, dopo una breve introduzione alla pubblicazione sicura, abbiamo visto perché non dovremmo avviare un thread all'interno di un costruttore.

Informazioni più dettagliate sulla pubblicazione e sulla fuga in Java possono essere trovate nel libro Java Concurrency in Practice.

Come al solito, tutti gli esempi sono disponibili su GitHub.