ProgrammationDéveloppeur Backend

Expliquez ce qu'est la publication sécurisée en Java, pourquoi elle est nécessaire en multithreading et comment l'assurer.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse

Publication Sécurisée (safe publication) — c'est une garantie de diffusion sécurisée d'un objet entre les threads : si un thread crée et initialise un objet, un autre thread verra toujours un objet entièrement construit et non son état partiellement initialisé.

Sans safe publication, on peut avoir une condition de concurrence : un thread voit une partie non initialisée de l'objet, même si le constructeur a déjà été exécuté.

Moyens d'assurer la publication sécurisée :

  • Utiliser des champs final — leur valeur est garantie d'être visible pour d'autres threads après le constructeur.
  • Publier l'objet via une variable volatile ou AtomicReference.
  • Publier l'objet via des conteneurs sûrs pour les threads (par exemple, des collections de java.util.concurrent).
  • Utiliser synchronized lors de la création et de la lecture de l'objet.

Exemple (non sécurisé) :

public class Holder { private int n; public Holder(int n) { this.n = n; } } Holder holder; void publish() { holder = new Holder(42); } // Sécurité non garantie

Peuvent être visibles des objets non entièrement construits !

Exemple (sécurisé) :

volatile Holder holder; void publish() { holder = new Holder(42); } // La lecture de holder sera également sécurisée

Question piège

Si tous les champs de l'objet sont final, cela garantit-il toujours la publication sécurisée ?

Réponse : Non, seulement si la référence au nouvel objet est publiée avant la fin du constructeur. Si la référence est partagée avec d'autres threads avant la fin du constructeur, les champs peuvent ne pas être complètement initialisés. Il faut éviter de publier this ou des références à des objets pas encore complètement construits pendant l'exécution du constructeur.

Exemple (dangereux !) :

public class PublishEscape { public static PublishEscape instance; public PublishEscape() { instance = this; // Mauvais ! this est publié avant la fin du constructeur } }

Histoire

Le développeur a assigné une référence à une instance de Service dans un champ accessible via static, avant la fin du constructeur. En conséquence, un autre thread a reçu un objet partiellement construit, provoquant un comportement imprévisible en production.


Histoire

Dans une application web, des objets singleton étaient créés sans synchronisation supplémentaire. Sous charge, certains threads obtenaient null ou des champs mal initialisés, entraînant des NullPointerExceptions intermittentes.


Histoire

Dans la bibliothèque, un List classique était utilisé pour le cache entre les threads — la publication sécurisée faisait défaut, et l'initialisation ne garantissait pas la visibilité pour les nouveaux threads. En fin de compte, le cache fonctionnait de manière chaotique, compromettant l'intégrité des données.