ProgrammazioneSviluppatore Backend

Spiega cos'è la safe publication in Java, perché è necessaria nella multithreading e quali modi ci sono per garantirla.

Supera i colloqui con l'assistente IA Hintsage

Risposta

La Safe Publication (pubblicazione sicura) è la garanzia di una diffusione sicura di un oggetto tra i thread: se un thread crea e inizializza un oggetto, un altro thread vedrà sempre l'oggetto completamente costruito, e non il suo stato parzialmente inizializzato.

Senza una safe publication, si possono verificare condizioni di competizione (race condition): un thread vede una parte non inizializzata dell'oggetto, anche se il costruttore ha già terminato.

Modi per garantire la safe publication:

  • Usare campi finali — il loro valore è garantito visibile ad altri thread dopo il costruttore.
  • Pubblicare l'oggetto attraverso una variabile volatile o AtomicReference.
  • Pubblicare l'oggetto attraverso contenitori thread-safe (ad esempio, collezioni di java.util.concurrent).
  • Usare synchronized durante la creazione e la lettura dell'oggetto.

Esempio (non sicuro):

public class Holder { private int n; public Holder(int n) { this.n = n; } } Holder holder; void publish() { holder = new Holder(42); } // Sicurezza non garantita

Possono essere visibili oggetti non completamente costruiti!

Esempio (sicuro):

volatile Holder holder; void publish() { holder = new Holder(42); } // La lettura di holder sarà anch'essa sicura

Domanda trabocchetto

Se tutti i campi nell'oggetto sono finali, garantisce sempre una safe publication?

Risposta: No, solo se il riferimento al nuovo oggetto viene pubblicato prima della fine del costruttore. Se il riferimento viene accesso da altri thread prima del completamento del costruttore, i campi potrebbero non essere completamente inizializzati. Si dovrebbe evitare di pubblicare this o riferimenti ad oggetti non completamente costruiti durante l'esecuzione del costruttore.

Esempio (pericoloso!):

public class PublishEscape { public static PublishEscape instance; public PublishEscape() { instance = this; // Male! this viene pubblicato prima della fine del costruttore } }

Storia

Uno sviluppatore ha assegnato un riferimento a un'istanza del Service in un campo accessibile tramite static, prima del completamento del costruttore. Di conseguenza, un altro thread ha ottenuto un oggetto parzialmente costruito, causando comportamenti imprevedibili in produzione.


Storia

In un'app web, gli oggetti singleton venivano creati senza sincronizzazione aggiuntiva. Sotto carico, alcuni thread ottenevano null o campi inizializzati in modo errato, causando Intermittent NullPointerException.


Storia

Nella libreria si utilizzava una normale List come cache tra i thread — mancava la safe publication, l'inizializzazione non garantiva la visibilità ai nuovi thread. Alla fine, la cache funzionava in modo caotico, compromettendo l'integrità dei dati.