Historiquement, l'initialisation paresseuse a été mise en place pour optimiser l'utilisation des ressources, lorsqu'un objet est créé uniquement lorsqu'il est réellement nécessaire. Cela était particulièrement important lors de la manipulation d'objets lourds, de connexions, de caches ou lors de la mise en œuvre de patterns complexes (par exemple, Singleton).
Le problème est que si tous les ressources sont créées simultanément, cela peut entraîner une consommation excessive de mémoire, de temps et de puissance, même lorsque l'objet n'est pas nécessaire. L'initialisation paresseuse résout le problème de la création "reportée".
La solution est généralement mise en œuvre par une vérification : si l'objet n'est pas encore créé, nous le créons, sinon, nous retournons celui existant.
Exemple de code :
public class LazyHolder { private Resource resource; public Resource getResource() { if (resource == null) { resource = new Resource(); } return resource; } }
Dans des conditions multithread, une synchronisation est nécessaire.
Caractéristiques clés :
Le Double-Checked Locking est-il une implémentation fiable pour l'initialisation paresseuse du Singleton ?
Ce n'est qu'à partir de Java 5, car auparavant, le modèle de mémoire ne garantissait pas cela. Il est nécessaire d'utiliser le mot-clé volatile pour le champ de ressource.
private volatile Resource resource; public Resource getResource() { if (resource == null) { synchronized(this) { if (resource == null) { resource = new Resource(); } } } return resource; }
A-t-il un sens de faire une initialisation paresseuse pour des objets légers ?
En général, non. Il est préférable de créer immédiatement des objets "légers" pour ne pas en réduire la lisibilité et ne pas compliquer le code avec une logique superflue.
L'initialisation paresseuse fonctionne-t-elle lors de la sérialisation d'objets ?
Pas toujours. Lors de la sérialisation, un état "ancien" peut être restauré, ou une logique supplémentaire pourra être nécessaire dans readObject().
Dans un service à haute charge, un pool spécial d'objets était analysé paresseusement, mais n'était pas synchronisé. Deux threads initialisaient simultanément l'objet, entraînant des fuites de mémoire et des erreurs imprévisibles.
Avantages :
Inconvénients :
Dans une grande application web, l'analytique est connectée uniquement via un appel API à travers un objet proxy initialisé paresseusement avec double-checked locking.
Avantages :
Inconvénients :